When it comes to configuring systems, many users are reluctant to change the default boot process. Visions of unbootable systems, inaccessible data, and reinstalls dance in their heads. Yes, it is good to be mindful of such things as they instill the necessary attention to detail you’ll need to use when making changes. However, once you’ve taken the necessary precautions, do take advantage of the hacks found in this chapter. Many of them will increase the security of your system.
This chapter also includes several password hacks. You’ll learn how to create an effective password policy and monitor compliance to that policy. You’ll find tools designed to assist you and your users in making good password choices. You’ll also learn how to configure OTP, an excellent choice for when you’re on the road and wish to access your network’s resources securely.
Configure a splash screen.
You’re not quite sure what you did to give the impression that you don’t already have enough to do. Somehow, though, you were elected at the latest staff meeting to create a jazzy logo that will appear on every user’s computer when they boot up in the morning.
While you may not be able to tell from first glance, the FreeBSD boot menu supports a surprising amount of customization. Let’s start by examining your current menu to see which tools you have to work with.
Your default boot menu will vary slightly depending upon your version of FreeBSD and whether you chose to install the boot menu when you installed the system. Let’s start with the most vanilla boot prompt and work our way up from there. In this scenario, you’ll see this message as your system boots:
Hit [Enter] to boot immediately, or any other key for command prompt. Booting [/boot/kernel/kernel] in 10 seconds...
FreeBSD 5.1 introduced a quasi-graphical boot menu that includes a picture of Beastie and the following options:
Welcome to FreeBSD! 1. Boot FreeBSD [default] 2. Boot FreeBSD with ACPI disabled 3. Boot FreeBSD in Safe Mode 4. Boot FreeBSD in single user mode 5. Boot FreeBSD with verbose logging 6. Escape to loader prompt 7. Reboot Select option, [Enter] for default or [Space] to pause timer 10
It is possible to get this menu without doing a full install of
FreeBSD 5.1. If you’re like me and use cvsup [Hack
#80] and buildworld
to keep up-to-date, you already
have the necessary files but need to do a bit of editing to enable
this boot menu. Even if you already have the boot menu, follow along
because we’re about to discover some of the logic behind the FreeBSD
boot process. This will be excellent preparation for learning how to
hack in your own customizations.
Let’s start by taking a look at the directory that contains all of the boot information. Not surprisingly, it’s called /boot:
# ls /boot -F
beastie.4th cdboot* kernel.old/ loader.rc support.4th
boot defaults/ loader* mbr
boot0 device.hints loader.4th modules/
boot1 frames.4th loader.conf pxeboot
boot2 kernel/ loader.help screen.4th
The actual file containing the new menu is beastie.4th. If your sources are out-of-date and you don’t have this file, you can download it from http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/boot/forth/. Be sure to download also the latest versions of frames.4th and screen.4th.
The /boot directory also
contains the loader
executable.
This application is responsible for finishing the boot process. To do
so, it depends on two configuration files, loader.rc
and loader.conf. Let’s take a
peek at loader.rc:
# more loader.rc
Loader.rc
$FreeBSD: src/sys/boot/forth/loader.rc,v 1.2 1999/11/24 17:59:37 dcs Exp $
Includes additional commands
include /boot/loader.4th
Reads and processes loader.rc
start
Tests for password -- executes autoboot first if a password was defined
check-password
Unless set otherwise, autoboot is automatic at this point
We’re aiming to be hackers here, not destroyers of systems. A system that refuses to boot completely is not a very fun system to work on. So, before mucking about with any of the files in /boot, make sure you have your Emergency Repair Kit ready (see [Hack #71] and [Hack #72] for more information). Also, take extra care in your editing and be especially alert for typos before saving your changes.
Lines that begin with a backslash () are comments. Additionally, you can add your own comments to
lines containing a command by preceding your comment with a
#
like this:
include /boot/loader.4th # do NOT remove this line! start # do NOT remove this line!
Those are good comments to add, as you want to make sure you never remove those two lines—they are necessary to the workings of your boot loader.
Before editing this file, make a backup copy first:
# cp loader.rc loader.rc.orig
Then, to tell your system to use beastie.4th, carefully add the following lines to the bottom of /boot/loader.rc.
Load in the boot menu include /boot/beastie.4th Do the normal initialization and startup initialize drop Start the boot menu beastie-start
Triple-check for typos. When you’re ready, make sure that you’ve saved all of your work and check that no one else is connected to the system. In order to test out the change, you’re going to have to reboot:
# reboot
If all went well, you now have a Beastie menu to assist you in your bootup selection. If your boss had something else in mind other than the ultracool Beastie menu, let him know that have you not yet begun to customize!
Remember the other file I mentioned, loader.conf? Well, you should actually have
two files with that name. /boot/defaults/loader.conf is the system
default, and you should never edit this file.
Instead, copy it over to /boot/loader.conf and make your changes
there. That way, not only do you have a chance to see what is
available for customization, you also reduce your risk of typos. Each
line in this file is commented and additional information can be
gleaned from man
loader.conf
.
Locate the Splash screen
configuration
section so you can configure that company logo
your boss keeps insisting on. This is what it looks like by
default:
splash_bmp_load="NO" # Set this to YES for bmp splash screen! splash_pcx_load="NO" # Set this to YES for pcx splash screen! vesa_load="NO" # Set this to YES to load the vesa module bitmap_load="NO" # Set this to YES if you want splash screen! bitmap_name="splash.bmp" # Set this to the name of the bmp or pcx file bitmap_type="splash_image_data" # and place it on the module_path
Obviously, we’ll have to change the NO
in one of those splash
lines to a YES
. Which one depends upon your picture
format. The two types of images that can be loaded are bmp
or pcx
. Depending upon the image you have to
work with, change the appropriate NO
to a YES
.
If the image also happens to have eight or more bits of color,
set vesa_load
to YES
. If you have no idea what type or size
of picture you’re dealing with, use the file
command:
# file logo.bmp
logo.bmp: PC bitmap data, Windows 3.x format, 408 x 167 x 8
This particular logo is a bitmap that is 408 167 pixels at 8 bits of color.
Don’t forget to set the path of your bitmap file, and make sure you remember to copy that bitmap to the specified location:
bitmap_name="/boot/logo.bmp"
Leave this line as is:
bitmap_type="splash_image_data" # and place it on the module_path
Finally, enable bitmap loading:
bitmap_load="YES"
When you’re editing /boot/loader.conf, keep in mind that you
are asking the loader program to load various portions of the kernel.
If you have changed your kernel configuration file [Hack
#54] , double-check that you haven’t stripped your
kernel of a function you’re now asking loader
to load. For example, before
rebooting I should double-check that splash
functionality is still in my kernel.
Here, my new kernel configuration file is named NEW:
# grep splash /usr/src/sys/i386/conf/NEW
device splash # Splash screen and screen saver support
splash
also requires device sc
, so ensure that is your console
type:
# grep -w sc /usr/src/sys/i386/conf/NEW
device sc
The -w
flag tells grep
to treat sc
as a word rather than attempt to match
any word containing the letters sc
.
Once you’re happy with your changes, make sure no one is working
on the system and then reboot
. Your
bitmap image should appear right after you make your choice at the
Beastie menu. It will remain on the screen until you press a key. This
behavior has the advantage of displaying your company logo instead of
the usual startup messages. However, if you ever need to see those
messages, simply press a key and your bitmap will disappear.
As it is set up now, the bitmap will also act as a terminal screensaver that will kick in after five minutes. To change the screensaver’s timeout value, add this line to /etc/rc.conf:
blanktime="60"
The number you choose represents the number of seconds. If you decide you don’t like the screensaver functionality, add this line to /etc/rc.conf:
saver="NO"
Those changes to /etc/rc.conf won’t take effect until you
reboot the system. To enforce those settings immediately, at least
until the next reboot, use the vidcontrol
command:
#vidcontrol -t 60
#vidcontrol -t off
Regardless of your timeout setting, you can still launch the screensaver at will—say, when you leave your terminal—by pressing the Shift and Pause keys simultaneously. You may just want to do that before you go grab your boss to show him that jazzy company logo.
man loader
man splash
/usr/share/examples/bootforth/ (bootloader examples for the experienced hacker who understands Forth)
The Boot section of the FreeBSD Handbook (http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/boot.html)
http://www.baldwin.cx/splash (splash images to get you started)
Thwart unauthorized physical access to a system.
Creating a snazzy boot environment for users is one thing. However, when it comes to booting up servers, your mind automatically shifts gears to security mode. Your goal is to ensure that only a very precious few on very rare occasions ever see the boot process on a server. After all, the golden rule in security land is “physical access equals complete access.”
Here’s a prime example—consider recovering from an unknown or forgotten root password. Go into the server closet, reboot that system, and press a key to interrupt the boot process to change the password. A few moments later, the system continues to boot as normal. This can be a real lifesaver if an admin leaves without divulging the root password. However, consider the security implications of an unauthorized user gaining physical access to that server: instant root access!
Let’s start by ensuring that regular users can’t reboot the system
either inadvertently or maliciously. By default, if a user presses
Ctrl-Alt-Delete, the system will clean up and reboot. Typically this
isn’t an issue for servers, as most administration is done remotely
and the server is safely locked away in a server closet. However, it
can wreak havoc on workstations, especially if the user is used to
working in a Windows environment and has become accustomed to pressing
Ctrl-Alt-Delete. It’s also worthwhile disabling on a server, as it
ensures that a person has to first become the superuser in order to
issue the reboot
command.
If you’re logged into a remote machine over SSH and try
Ctrl-Alt-Delete, it will affect your own machine, not the remote
machine. reboot
works well over
the network, though.
Disabling this feature requires a kernel rebuild. (See [Hack #54] for detailed instructions.) Add one of these lines to your kernel configuration file, then rebuild and reinstall the kernel:
options SC_DISABLE_REBOOT # if using syscons console driver # or options PCVT_CTRL_ALT_DEL # if using pcvt console driver
You’re probably thinking, “If I wanted to reboot a system and didn’t know the superuser password, I’d simply hit the power button.” Yup! That kernel option certainly won’t prevent that, but a carefully thought out CMOS[1] configuration will decide if and how that system will reboot.
At a minimum, the CMOS configuration should allow only one boot device. This is to prevent an intruder from trying to boot an alternate kernel from a floppy, CD-ROM drive, or other supported boot device. Additionally, you should set a password for CMOS and record it in a safe place. This will prevent an intruder from simply changing the CMOS configuration. Keep in mind that this is not fail-proof; you are merely adding layers of inconvenience. A determined intruder can simply pop open the case and drain the CMOS battery, but that takes time and additional effort.
All the magic happens when you interrupt the boot process. This is where you can change the superuser password without having to first know the superuser password. This is where you can unload the currently loaded kernel and replace it with another. This is where you can change any configuration file or binary without worrying about securelevels or system flags [Hack #56] . This is the reason why you lock up your servers, monitor access to the server room, and run them headless [Hack #26] .
Fortunately, interrupting the boot process requires keyboard input, meaning the user needs physical access to the system. What happens when a malicious user does bypass your physical security measures, gaining physical access to the system? All she has to do is interrupt that boot process, and the system is hers to do as she wishes.
On a system without the graphical boot menu [Hack
#24] , pressing any key at the timer will pause the
boot process. If the system has the graphical boot menu, pressing
6
to Escape to loader prompt
will show the same
timer. The timer option looks like this:
Hit [Enter] to boot immediately, or any other key for command prompt. Booting [/boot/kernel/kernel] in 10 seconds...
If you press any key other than Enter, you’ll receive this:
Type '?' for a list of commands, 'help' for more detailed help.
OK boot -s
Type boot -s
to enter
single-user mode. The kernel will appear to load normally, but,
instead of processing the rc
scripts, this prompt will appear:
Enter full pathname of shell or RETURN for /bin/sh: #
Once you’ve finished making your desired changes, simply type
exit
. The system will continue to
boot into multiuser mode.
Now, how do you prevent a user from doing that? Password protect single-user mode by editing /etc/ttys. Find this line:
# If console is marked "insecure", then init will ask for the root password # when going to single-user mode. console none unknown off secure
Follow the comments and change the word secure
to insecure
. While that may seem nonintuitive,
you’re saying the system is considered to be insecure, thus you want a
password. The next time a user attempts single-user mode, the kernel
will load, but the user will receive this prompt instead:
Enter root password, or ^D to go multi-user Password:
Let’s return to the timer section of the boot process. A user
can type more than boot -s
after
interrupting the boot process. In fact, if you press ?
at that OK
prompt, you’ll see that you can unload
the current kernel, load another kernel, load and unload kernel
modules, and view and change variables. You can muck about with just
about every part of the boot process that would normally be controlled
by the loader
command.
Fortunately, you can also require a user to input a password
before receiving that OK
prompt.
Set the password by adding this line to /boot/loader.conf:
password=12345
Of course, your password should be harder to guess than 12345
. Now the boot process will prompt the
user for a password. Without that password, you cannot enter
single-user mode or load or unload kernel modules. You can still boot;
you just cannot interrupt the boot process.
Also, if your CMOS supports it, you can require a password to boot the machine. However, this is often considered to be a bad thing, especially on a co-located web or mail server.
man boot
man loader
The Boot Process section of the FreeBSD Handbook (http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/boot-blocks.html)
Resetting the Root Password in the FreeBSD FAQ (http://www.freebsd.org/doc/en_US.ISO8859-1/books/faq/admin.html#FORGOT-ROOT-PW)
For those times when you want to run a system “headless."
Sometimes it is a simple matter of economy. Perhaps you’ve managed to scrounge up another system, but you don’t have enough monitors, keyboards, or mice to go around. You also don’t have the budget to purchase either those or a KVM switch. Sometimes it is a matter of security. Perhaps you’re introducing a PC to a server closet and your physical security policy prevents server closet devices from being attached to monitors, keyboards, and mice.
Before you can run a system “headless,” you need to have an alternative for accessing that system. Once you’ve removed input and output peripherals, your entry point into the system is now either through the network card or a serial port.
Going in through the network card is the easiest and is quite secure if you’re using SSH. However, you should also consider a plan B. What if for some reason the system becomes inaccessible over the network? How do you get into the system then? Do you really want to gather up a spare monitor, keyboard, and mouse and carry them into the server closet?
A more attractive plan B may be to purchase a null modem cable as insurance. This is a crossed serial cable that is designed to go from one computer’s serial port to another computer’s serial port. This type of cable allows you to access a system without going through the network, which is a real lifesaver when the system isn’t responding to the network. You can purchase this type of cable at any store that sells networking cables.
Your last consideration is whether the system BIOS will cooperate with your plan. Most newer BIOSes will. Many have a CMOS option that can be configured to disable “halt on errors.” It’s always a good idea to check out your available CMOS options before you start unplugging your peripherals.
I’ve just installed a new FreeBSD 5.1 system. Since I didn’t have a null modem cable handy, I installed the old-fashioned way with the monitor and keyboard attached. If you do have a null modem cable and want to experiment with a headless install, follow the directions in the Handbook section referenced at the end of this hack.
Since I want to access the server over the network, I’ll
double-check that the NIC is properly configured and that sshd
is running:
%ifconfig ed0
ed0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500 inet 192.168.2.94 netmask 0xffffff00 broadcast 192.168.2.255 ether 00:80:ad:79:4e:fd %sockstat | grep sshd
root sshd 389 4 tcp4 *:22 *:*
The ifconfig
command is used
to verify an interface’s configuration; in this example, the interface
is ed0
. The flags
indicate that this interface is
UP
and RUNNING
. The interface also has an IP
address of 192.168.2.94
.
The sockstat
command is
similar to the netstat
command, but
I find it provides a more intuitive output. For each open port it will
display the owner of the service (root
), the name of the service (sshd
), the PID (389
), the socket file descriptor (4
), the transport (tcp4)
, the local address (*:22
), and the foreign address (*.*
).
The PID is useful if you need to send a signal to the process.
The local address indicates which interfaces on this system (in this
case, all, or *
) are listening on
which port number (22
). There
aren’t any current sessions, as the foreign address section is
*.*
. If there were a current
session, it would show the address of the other system followed by the
socket number being used for the connection.
If for some reason sshd
isn’t
running on your system, add the following line to /etc/rc.conf:
sshd_enable="YES"
and double-check that it’ll be available at bootup, like so:
# /etc/rc.d/sshd rcvar
#sshd
$sshd_enable=YES
Finally, typing sshd
as the
superuser should start the daemon. You can prove this by checking that
it’s listening with sockstat | grep
sshd
.
One last test—I’ll make sure I can log into the system over the network:
% ssh 192.168.2.94
Password:
%
Now that I knew the system was accessible over the network, it
was time for the moment of truth. After halt
ing the system, I entered its CMOS
configuration. I was a little bit worried because there weren’t any
options dealing with “halt errors.” Undaunted, I left CMOS and powered
off and unplugged the monitor, keyboard, and mouse. I then opened the
case and physically removed the video card.
When I powered up, the system responded with a longer than
ordinary beep. But after a few seconds, my hard drive light flashed
and I could hear the operating system probing my devices and loading
the drivers. After a moment or so, I tried to ssh
into the system and was greeted with my
password prompt! Assuming your BIOS is willing to cooperate, FreeBSD
has no problem loading headless.
Should your system ever stop responding over the network, you’ll be glad you purchased that null modem serial cable. Connect one end to the COM port of the headless system, and the other end to the COM port of another system that you can access either directly or over the network.
If that other system is running a Windows operating system, go to Start → Programs → Accessories → Communications → HyperTerminal (or open hypertrm.exe). You’ll need to create a new connection, so choose a name and icon for it. Under Connect using:, choose the COM port to which the serial cable is attached.
You’ll also have to configure the port properties for that COM
port. Change the default 2400 bits per second to 9600. Finally, change
hardware flow control to none
.
Press Enter, and you should be connected to the headless system. If
you’re not, double-check that you chose the correct COM port.
If you’re attaching from a system running any variant of Unix,
you can use either the cu
or
tip
commands to connect via the
serial cable.
To use cu
, simply
specify your COM port using the line switch -l
and a speed of 9600 baud using the speed
switch -s
. For example, this syntax
allows you to connect to COM2 or cuaa1
:
# cu -l /dev/cuaa1 -s 9600
Connected.
You should now be able to see what is happening on your headless system. One of the advantages of connecting through a serial cable is that you can watch the boot process of the system. You can’t do this over a network connection, because initializing the network occurs toward the end of a successful boot.
Before the network can be initialized, the kernel must successfully load into memory and the necessary hardware must be probed. If you’re having problems booting a system, it is usually due to a missing or corrupt kernel or a hardware problem.
To disconnect from the cu
session, type ~
., then press the
Enter key. You should receive a Disconnected
. message and receive the prompt
of the system you started from.
The tip
utility doesn’t use line or speed switches. It instead
expects you to use one of the finger friendly shortcuts found at the
end of the /etc/remote file.
Let’s take a look at that section:
# tail /etc/remote
# Hardwired line
cuaa0b|cua0b:dv=/dev/cuaa0:br#2400:pa=none:
cuaa0c|cua0c:dv=/dev/cuaa0:br#9600:pa=none:
# Finger friendly shortcuts
com1:dv=/dev/cuaa0:br#9600:pa=none:
com2:dv=/dev/cuaa1:br#9600:pa=none:
com3:dv=/dev/cuaa2:br#9600:pa=none:
com4:dv=/dev/cuaa3:br#9600:pa=none:
Notice that there is an entry for each COM port. This means that to connect to COM2, you simply have to type:
# tip com2
connected
You need a little bit more coordination to disconnect, though.
Hold down Shift while you press the ~
key. Keep your finger on Shift as you
press the Ctrl key, then the letter D:
# ~^D
[EOT]
man tip
man cu
The Advanced Installation Guide in the FreeBSD Handbook (http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/install-advanced.html)
More on headless systems, but this time from the NetBSD perspective.
We’ve already seen in [Hack #26] that it’s important to have an alternative method for connecting to a headless server. It’s also important to be able to receive a headless system’s console messages. This hack will show how to configure both on a NetBSD system.
If you have another machine close to your headless server, it
may be convenient to enable the serial console so that you can connect to it using a
serial communication program. tip
,
included in the base system, and minicom
, available through the packages collection, allow you
to handle the server as if you were working on a real physical
console.
To enable the serial console under NetBSD, simply tell the
bootblocks to use the serial port as the console; they will configure
the kernel on the fly to use it instead of the physical screen. You
also need kernel support for the serial port device, which is included
in the default GENERIC
kernel.
However, changing the bootblocks configuration is a bit tricky because you
need write permissions to the raw root device. As we are talking about
a server, I assume the securelevel functionality is enabled; you
must temporarily disable it by adding the
options INSECURE
line to your
kernel. While in the kernel configuration file, double-check that it
includes serial port support. Then, recompile your kernel.
Once you have access to the raw partition, update the bootblocks
using the installboot
utility. The process depends on the NetBSD version you
are using.
If you are running 2.0 or higher, use the command shown next. Replace the bootxx_ffsv1 file with the one that matches your root filesystem type; failure to do so will render your system unbootable.
# /usr/sbin/installboot -o console=com0 /dev/rwd0a /usr/mdec/bootxx_ffsv1
If you are running 1.6, use the following command instead:
# /usr/mdec/installboot /usr/mdec/biosboot_com0.sym /dev/rwd0a
When done, rebuild your kernel without the options INSECURE
line to reenable
securelevel. You can also remove the console drivers wscons
and pccons
to reduce the kernel size, though you
must keep the serial port driver.
Even if you have configured a serial console, you won’t
always be connected to it. Therefore, it is very convenient to
redirect important console messages to another machine that has a
physical screen connected to it. syslogd
lets you do this.
Start by allowing incoming syslogd
connections on the machine that will be receiving log
messages. (I call mine logger.local
.) To do this, add the following
lines to /etc/rc.conf:
syslogd_enable="YES" syslogd_flags="-ss"
The first option is not really needed, as syslogd
is enabled by default. The second
option overrides the secure (s
)
flag that otherwise would be passed to the daemon through /etc/defaults/rc.conf. This flag tells
syslogd
not to listen on a UDP
socket, and in this scenario we want to receive log messages over the
network.
Then, restart the daemon:
# /etc/rc.d/syslogd restart
logger.local
can now receive
incoming syslogd
connections from
any host. If required, you can restrict this by using the built-in
firewall, ipf
.
You are ready to configure your headless server to send
messages to the logger machine. As an example, we are going to
redirect all messages that are actually sent to the serial console to
logger.local
.
Open /etc/syslog.conf in
your favorite editor. You will notice that the first uncommented line
directs messages to /dev/console.
Append the @logger.local
string to
it, separated by a comma. After the changes, you should end up with
something like:
*.err;kern.*;auth.notice;authpriv.none;mail.crit /dev/console,@logger.local
Repeat for any other categories you want to redirect. When done,
restart syslogd
as shown earlier.
If you are running a headless system at home, you may want
to shut it down at night. You could do this by ssh
ing into the server and executing
shutdown
manually, but this
requires a second system. However, since you have physical access to
the headless system, you can simply use wsmoused
, which will let you execute two or
three commands from a mouse—one for each mouse button.
wsmoused
’s “action mode” lets
you assign commands to mouse buttons. Here’s a sample configuration
file to shut down and reboot the machine, which you can copy to
/etc/wsmoused.conf:
device = /dev/wsmoused; modes = action; mode action { button_0_down = "shutdown -p now"; button_2_down = "shutdown -r now"; }
Here I’ve mapped the left mouse button, 0
, to the command that will halt the system
and the right mouse button, 2
, to
the command that will reboot the system. (The middle mouse button is
1
.) Since I don’t plan on using
this mouse for its usual input functions, such as copy and paste, this
is a really convenient way to power off the system quickly and
safely.
Enable the startup of wsmoused
at boot time:
# echo "wsmoused=YES" >> /etc/rc.conf
Some headless servers don’t support APM or ACP, so the kernel
can’t power them down automatically. The i386 architecture has another
option: beep on halt. It beeps the speaker multiple times when it is
safe to power off the machine after a successful halt
.
To enable this feature, add the following line to your kernel configuration file and rebuild it:
options BEEP_ONHALT
In case you do not like the default tone, you have several other options. Here they’re shown with their default values:
options BEEP_ONHALT_COUNT=3 # Times to beep options BEEP_ONHALT_PITCH=1500 # Default frequency (in Hz) options BEEP_ONHALT_PERIOD=250 # Default duration (in msecs)
man 8 installboot
man syslogd
man wsmoused
man shutdown
Give users the information you want them to receive when they log in.
The default login process on a FreeBSD system produces a fair bit of
information. The terminal message before the login
prompt clearly indicates that the
machine is a FreeBSD system. After logging in, a user will receive a
copyright message and a Message of the Day (or motd
), both of which contain many references
to FreeBSD.
This may or may not be a good thing, depending upon the security requirements of your network. Your organization may also require you to provide legal information regarding network access or perhaps a banner touting the benefits of your corporation. Fortunately, a few simple hacks are all that stand between the defaults and your network’s particular requirements.
Let’s start with the copyright information. That’s this part of the default login process:
Copyright (c) 1992-2003 The FreeBSD Project. Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994 The Regents of the University of California. All rights reserved.
To prevent users from seeing this information, simply:
# touch /etc/COPYRIGHT
Technically, you could add your own information to /etc/COPYRIGHT instead of leaving it as an empty file. However, it is common practice to put your information in /etc/motd instead. The default /etc/motd contains very useful information to the new user, but it does get rather old after a few hundred logins.
You can edit /etc/motd to say whatever suits your purposes—anything from your favorite sci-fi excerpt to all the nasty things that will happen to someone if they continue to try to log into your system. Here’s a very simple example:
# more /etc/motd
*********************************************************
***** Authorized users only!! *****
*********************************************************
You’ll note that after you customize your motd
, users will still see this text
prepended to it:
FreeBSD 5.1-RELEASE (GENERIC) #0: Thu Jun 5 02:55:42 GMT 2003
If you don’t want to advertise your operating system version and kernel information, you’ll need one more hack. Add this line to /etc/rc.conf:
update_motd="NO"
If you’re using FreeBSD 5.x, you no longer have to reboot
or go into single-user mode to
initialize a change to /etc/rc.conf. Instead, you can use one of
the many scripts available in /etc/rc.d. Let’s see if there’s a script
that deals with motd
:
# ls -F /etc/rc.d | grep motd
motd*
Excellent. Let’s see what syntax that command expects:
# /etc/rc.d/motd
Usage: /etc/rc.d/motd [fast|force](start|stop|restart|rcvar)
Parameters in square brackets are optional, whereas parameters
in parentheses are mandatory. Notice each option is separated by the
or symbol (|
), meaning you just pick one out of the
list. In our case, we want to use the rcvar
parameter. This will tell the motd
script to reread its setting in
/etc/rc.conf:
# /etc/rc.d/motd rcvar
# motd
$update_motd=NO
Finally, let’s change the text that first appears at the login prompt. This requires an edit to /etc/gettytab. This is a fairly important file as it controls access to your terminals, which is how users access the system. Before editing this file, always make a backup copy first:
# cp /etc/gettytab /etc/gettytab.orig
Next, open up /etc/gettytab in your favorite text editor and look for this line:
default:
:cb:ce:ck:lc:fd#1000:im=
%s/%m (%h) (%t)
:sp#1200:
See the part in bold? That’s the part you can replace with what you’d like the world to see when they receive their login prompt. Right now, they see this:
FreeBSD/i386 (host.domain.com) (ttyv1)
That’s because that default string contains the variables in Table 3-1.
Variable | Meaning |
%s | Operating system |
%m | Architecture |
%h | Hostname |
%t | tty name |
You can very carefully change those characters to something else. For example, mine looks like this:
:cb:ce:ck:lc:fd#1000:im=
I'm a node in Cyberspace. Who are you?
:sp#1200:
Again, I’ve put my changes in bold for emphasis. Carefully
double-check that you didn’t lose any carriage return (
) or newline (
) characters along the way, then save your
change.
It’s important to test your change immediately at a different terminal to ensure you can still log into your system. This way, if you did make a typo that prevents logins, you can return to your previous terminal and fix it.
I’ll press Alt-F4 to go to a terminal with a login prompt. I’ll probably still see the old terminal message, so I’ll log in, log out, then log in again:
login:
Password:
% exit
logout
I'm a node in Cyberspace. Who are you?
login:
man motd
man gettytab
The /etc/rc.d section of the FreeBSD Handbook (http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/configtuning-rcng.html)
Take these simple steps to thwart password crackers.
All good administrators know that passwords can be a weak link in the security chain. A malicious and determined user armed with a password cracker could conceivably guess enough of your network’s passwords to access unauthorized resources.
Fortunately, you can make a password cracker’s life very difficult in several ways. First, educate your users to choose complex, hard-to-guess passwords that are meaningful enough for them to remember. This will thwart dictionary password crackers [Hack #30] , which use lists of dictionary and easy-to-guess words.
Second, be aware of who has superuser privileges and who has the right to backup /etc. This directory contains the two password databases that are required to run a brute-force password cracker. As the name implies, this type of cracker will eventually guess every password in your password databases as it systematically tries every possible keyboard combination. Your best protection from this type of cracker is to prevent access to those password databases. This includes locking up your backup tapes and monitoring their access.
It is also a good idea to increase the amount of time it would take a brute-force cracker to crack a password database. FreeBSD, like most Unix systems, adds a magic bit of randomness—known as a salt—to the password when it is stored in the password database. The upshot is that a password cracker may have to try up to 4,096 different combinations for each and every password it tries to guess.
Using a strong algorithm to protect your passwords can also slow down a brute-force cracker. FreeBSD supports a hard-to-crack algorithm known as Blowfish. One of the first things I do after a FreeBSD install is to configure the password database to use Blowfish. While it is easier to do this before you create your users, it is still worth your while to implement it after you’ve created your user accounts.
To use Blowfish, start by opening up /etc/login.conf in your favorite editor. Look for this line:
:passwd_format=md5:
Carefully edit it so it looks like this:
:passwd_format=blf:
Check for typos before saving your change.
You may have noticed this comment when you modified /etc/login.conf:
# Remember to rebuild the database after each change to this file: # # cap_mkdb /etc/login.conf #
Let’s take a closer look at what we’re being asked to do.
According to that comment, login.conf is more than a configuration
file, it is a database. Not only that, it is a capability
database, a database that supports different capabilities.
That is the reason behind the weird syntax within login.conf. Whenever you edit a capability
database, you have to use the cap_mkdb
command to integrate your changes within the
database.
So, follow the directions:
# cap_mkdb /etc/login.conf
If you have any existing users, you need to convert their passwords from MD5 to Blowfish. This is why it’s a good idea to make the change before you create your users.
If you’ve already created users, it’s back to the password
database to find all of the active accounts. Inactive
accounts—accounts that don’t allow logins—have the *
character instead of an encrypted
password. Since we want to find all of the lines in the password
database that do not contain an asterisk, we
need an inverted grep
:
# grep -v '*' /etc/master.passwd
root:$1$ywXbyPT/$GC8tXN91c.lsKRpLZori61:0:0::0:0:Charlie &:/root:/bin/csh
dru:$1$GFm1nh6I$jh3v4I.QNf450ARgltZU5.:1008:0::0:0:User &:/home/dru:/bin/csh
Well, that worked, but we could make the output look much prettier:
# grep -v '*' /etc/master.passwd | cut -d ':' -f 1
root
dru
Let’s pick apart that command syntax. grep -v
creates a reverse filter. In
effect, it says, “Show me the lines in /etc/master.passwd that do
not contain an *
.” Since those lines are long and contain
much more than just the username, I piped the output to the cut
utility to literally cut out the
portions I don’t need to see. Notice that the usernames are the very
first thing in each line, and they are always followed by the :
field separator. -d
tells
cut
to consider the colon
character, not the tab character, as the separator. -f 1
tells cut
that I’m interested in the very first
field of that line.
It looks like my particular system has two active accounts:
root
and dru
. Notice in the original output the
long sequence of characters that starts with $1
and ends with :. No, my users’
passwords aren’t quite that complex. Rather, you’re seeing the
password after it’s been encrypted by the MD5 algorithm. That
$1
means MD5. It’ll be $2
after we switch to Blowfish encryption.
(Be aware that you can’t edit the file directly; the entire password
must be changed.)
I’ll now change those two passwords:
#passwd dru
Changing local password for dru New Password: Retype New Password: #passwd
Changing local password for root New Password: Retype New Password:
Note that the superuser can change any user’s password by specifying the appropriate username. If you don’t specify a name, you will instead change the root password.
When you’re finished, repeat the original grep -v
command and double-check that all
of the encrypted passwords now start with $2
.
Finally, configure the adduser
utility to use Blowfish whenever you create a new
user by editing /etc/auth.conf.
Look for this line:
# crypt_default = md5 des
and carefully change it to:
crypt_default = blf
Once you’ve saved your change, test it by creating a new user.
The easiest way to do this is to type adduser
and follow the prompts.
man passwd
man adduser
Blowfish information by Bruce Schneier, the creator of the algorithm, at http://www.schneier.com/blowfish.html
When to use a password cracker utility.
Now that you’ve tightened up your password policy to thwart password crackers, it’s time to learn how to use a password cracker to monitor the effectiveness of that password policy.
You’re probably thinking, “Hey, wait a minute! Isn’t that some sort of oxymoron? An administrator cracking passwords?” Well, it depends upon the type of password cracker you plan on using.
A brute-force password cracker such as John the ripper
or slurpie
will systematically try every possible
keyboard combination until it has cracked every password in the password
database. Does an administrator need to know every password in his
network? Definitely not.
However, an administrator does need to know if her users are
choosing easy-to-guess passwords, especially if she’s responsible for
enforcing compliance to the network’s password policy. A properly
tweaked dictionary password cracker such as crack
is an effective way to monitor that
compliance.
It is important that a network’s security policy indicates in writing who runs the dictionary cracker, when it is run, and how the results are handled. For example, if the password policy forces users to change their passwords every 30 days, the following day is an excellent time for the delegated administrator to run the cracker. Ideally, the cracker will return no results. This means all users chose a strong password. Should the cracker find some weak passwords, the security policy should clearly outline the procedure used to ensure that noncompliant users change their passwords to ones that are harder to guess.
Let’s take a look at the most commonly used dictionary password cracker used on Unix systems,
crack
. You’ll have to be the
superuser for this entire hack because, fortunately, only the
superuser has permission to crack the passwd
database. crack
should build on any Unix system; I’ll
demonstrate on FreeBSD:
#cd /usr/ports/security/crack
#make install clean
On my system, this creates the /usr/local/crack directory which only the
superuser can access. I need to cd
into that directory in order to crack passwords. I’ll start with a
simple crack, then show you how to tweak this utility to serve your
particular network.
#cd /usr/local/crack
#./Crack -fmt bsd /etc/master.passwd
Crack
is a Bourne shell
script contained within this directory, so you’ll have to run it with
the command ./Crack
. Use the
-fmt
switch to indicate the type of
system; in my case, it is bsd
.
Finally, pass the path of the database containing the actual password
hashes. On my system, this is the BSD shadow password database at
/etc/master.passwd. The command
and output on my test system is:
# ./Crack -fmt bsd /etc/master.passwd
Crack 5.0a: The Password Cracker.
(c) Alec Muffett, 1991, 1992, 1993, 1994, 1995, 1996
System: FreeBSD genisis 5.1-RELEASE FreeBSD 5.1-RELEASE #7:
Tue Jul 29 09:54:11 EDT 2003 dru@genisis:/usr/obj/usr/src/sys/NEW i386
Home: /usr/local/crack
Invoked: ./Crack -fmt bsd /etc/master.passwd
Stamp: freebsd-5-i386_
Crack: making utilities in run/bin/freebsd-5-i386_
find . -name "*~" -print | xargs -n50 rm -f
( cd src; for dir in * ; do ( cd $dir ; make clean ) ; done )
rm -f dawglib.o debug.o rules.o stringlib.o *~
/bin/rm -f *.o tags core rpw destest des speed libdes.a .nfs* *.old
*.bak destest rpw des speed
rm -f *.o *~
`../../run/bin/freebsd-5-i386_/libc5.a' is up to date.
all made in util
Crack: The dictionaries seem up to date...
Crack: Sorting out and merging feedback, please be patient...
Crack: Merging password files...
Crack: Creating gecos-derived dictionaries
mkgecosd: making non-permuted words dictionary
mkgecosd: making permuted words dictionary
Crack: launching: cracker -kill run/Kgenisis.27478
Done
Note that the word Done
is a
bit of a misnomer. The gecos
test
is finished, but the actual dictionary attack has just begun and is
quietly perking along in the background:
# ps -acux | grep cracker
root 14013 97.0 2.8 9448 8916 v5 R 10:32AM 4:17.68 cracker
Let’s take a look at my current results, then analyze what is happening here:
# ./Reporter -quiet
---- passwords cracked as of Mon Nov 17 10:33:18 EST 2003 ----
1069099872:Guessed test [test] User & [/etc/master.passwd /bin/csh]
---- done ----
The Reporter
script, which is also found in the /usr/local/crack/ directory, sends the
current results of the dictionary crack to standard output. I ran
Reporter
shortly after Crack
had returned my prompt. Notice that
it found that the password for the test
account was test
.
The reason why it found this password so quickly is because of
the gecos
field in /etc/master.passwd. If you’re familiar
with man master.passwd
, you know
that the gecos
field contains the
user’s full name, possibly followed by her extension, office phone
number, and home phone number. This means that if a user uses any of
those values for a password, her password can be cracked within a
second or two.
The actual dictionary attack will take a while to run. How
long will depend upon the speed of your CPU. However, you should
expect crack
to run for a good
portion of a business day.
Why so long? If you’ve ever had the opportunity to run a dictionary cracker on a non-Unix system, you may have had your results back in well under an hour. The answer is that BSD password hashes are protected by a salt. In simple terms, the salt adds random characters to a user’s password before the encryption algorithm creates the hash. Those are encrypted hashes, not the actual passwords, stored in /etc/master.passwd. In order for the password cracker to bypass the salt, it has to try many variations of the same word before it can determine if that word is indeed the user’s password.
You may want to write a script that will tell you when
Crack
is finished. Here is a
simple example:
#!/bin/sh #script to see if Crack is still running #and to display current report while ps -acux | grep -l "cracker" > /dev/null do sleep 600 echo "Still running. Here's the latest report:" cd /usr/local/crack && ./Reporter -quiet done echo "Execution is complete."
This script uses a simple while
loop that runs every ten minutes
(600 seconds). If cracker
still
shows up as a running process in the ps
output, the ./Reporter -quiet
script will run.
Otherwise, the script ends, printing Execution is complete
.
If you’d like to receive a pop-up message showing the results of the script, see [Hack #100] .
Your security policy should also provide guidelines on how to
clean up after crack
finishes.
The program stores several working files in the run
subdirectory. They will all have a
numeric extension:
# ls run
D.boot.69783 Egenisis.69783 bin/
Dgenisis.69783 Kgenisis.69783 dict/
When you remove those files, ensure you leave the subdirectories intact:
#cd run
#rm *.69783
#ls
bin/ dict/
Once you implement regular dictionary cracks, you’ll find
that after a few months, your users will start to consistently choose
strong passwords. However, bear in mind that a dictionary cracker is
only as good as its dictionaries. The dictionaries that come with
crack
are a good start if your
users speak English.
Let’s start by seeing what dictionaries crack
included:
# ls dict/1/
abbr.dwg list.dwg
assurnames.dwg male-names.dwg
asteroids.dwg movies.dwg
bad_pws.dat.dwg myths-legends.dwg
biology.dwg names.french.dwg
cartoon.dwg numbers.dwg
chars.dwg other-names.dwg
common-passwords.txt.dwg paradise.lost.dwg
crl.words.dwg phrases.dwg
dosref.dwg places.dwg
family-names.dwg python.dwg
famous.dwg roget.words.dwg
fast-names.dwg sf.dwg
female-names.dwg sports.dwg
given-names.dwg trek.dwg
jargon.dwg unix.dict.dwg
junk.dwg yiddish.dwg
lcarrol.dwg
Notice that each built-in dictionary ends with a dwg extension. However, crack
understands any dictionary or word
list, even if it is compressed (i.e., its filename ends in either
.Z or .gz).
If you use the file
command
on the dwg files, you’ll find
that each file is ASCII text. Mind you, the contents don’t look like
the average dictionary file:
# head abbr.dwg
#!xdawg
02bon2b
04sa7ya
0bbroyg
6bvgw
0egbdf
0fsasya
0gok
0oottfogvh
0roygbiv
Don’t worry, those aren’t the actual words. Instead, the numbers
sort the words by likelihood. That is, the words don’t appear in
alphabetical order, but rather in the order they’re likely to appear
as a password. For example, the word password
is much more likely to be used as a
password than pasul
.
If your users speak other languages, consider downloading additional dictionaries. Start at the Cerias site mentioned at the end of this hack. It’s well worth your while to browse through the site’s dictionaries, local, and wordlists subdirectories looking for dictionaries that suit your particular needs.
Let’s go there now and check out the possible word lists:
#ftp ftp.cerias.purdue.edu
Connected to ftp.cerias.purdue.edu. <snip long banner> Name (ftp.cerias.purdue.edu:dru):anonymous
331 Guest login ok, send your complete e-mail address as password. 230 Logged in anonymously. Remote system type is UNIX. Using binary mode to transfer files. ftp>cd pub/dict/wordlists
250 "/pub/dict/wordlists" is new cwd. ftp>ls
227 Entering Passive Mode (128,10,252,10,169,45) 150 Data connection accepted from 1.2.3.4:49460; transfer starting. -rw-rw-r-- 1 ftpuser ftpusers 1971 Jun 14 2000 README.gz drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 aussie drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 chinese drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 computer drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 danish drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 dictionaries drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 dutch drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 french drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 german drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 italian drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 japanese drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 literature drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 movieTV drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 names drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 norwegian drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 places drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 random drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 religion drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 science drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 spanish drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 swedish drwxrwxr-x 2 ftpuser ftpusers 4096 Jun 14 2000 yiddish 226 Listing completed.
My network includes several French-speaking users, so I’ll take a look at the French word list:
ftp>cd french
250 "/pub/dict/wordlists/french" is new cwd. ftp>ls
227 Entering Passive Mode (128,10,252,10,175,158) 150 Data connection accepted from 1.2.3.4:49530; transfer starting. -rw-rw-r-- 1 ftpuser ftpusers 332537 Jun 14 2000 dico.gz 226 Listing completed.
Before downloading the word list, I’ll use the local change directory command to ensure I’m downloading the file to the correct directory on my system:
ftp>lcd /usr/local/crack/dict/1
Local directory now /usr/local/crack/dict/1 ftp>get dico.gz
local: dico.gz remote: dico.gz 227 Entering Passive Mode (128,10,252,10,175,160) 150 Data connection accepted from 1.2.3.4:49531; transfer starting for dico.gz (332537 bytes). 226 Transfer completed. 332537 bytes received in 00:02 (142.24 KB/s) ftp>bye
221 Goodbye.
Now that I have a new word list in /usr/local/crack/dict/1/, I’ll run the following command:
#cd /usr/local/crack
#make rmdict
#rm -rf run/dict
That’s it. The next time I run ./Crack
, I’ll see the following message
appended to the usual Crack
message:
Crack: making dictionary groups, please be patient... doing group 1... doing group 2... doing group 3... mkdictgrps: uniq'ing dictionary groups... group 1 and 2... group 1 and 3... group 2 and 3... mkdictgrps: compressing dictionary groups... Crack: Created new dictionaries... Crack: Sorting out and merging feedback, please be patient... Crack: Merging password files... Crack: Creating gecos-derived dictionaries mkgecosd: making non-permuted words dictionary mkgecosd: making permuted words dictionary Crack: launching: cracker -kill run/Kgenisis.55941 Done
This indicates that crack
has
found the new dictionary and is merging it into its logic.
The crack
web site (http://www.crypticide.org/users/alecm)
The Cerias FTP site containing cracker dictionaries (ftp://ftp.cerias.purdue.edu/pub/dict/)
Traditionally, it has been difficult for a Unix administrator to create and enforce a reusable password policy. Fortunately, PAM addresses this.
If you’re using FreeBSD 5.0 or higher, your system has a PAM (Pluggable Authentication Modules) module specifically designed to assist in the creation and enforcement of a reusable password policy. If you’re running a different version of BSD, see the end of this hack for other sources for this module.
Before using this module, spend some time reading man
pam_passwdqc
, as it
thoroughly covers each option and its possible values. Any values
contained within parentheses are defaults. As you read through this
manpage, compare those defaults with your own network’s security
policy and make note of any values that will require a change.
This PAM module is fairly comprehensive, allowing you to enable many of the features expected in a password policy. Here’s an overview of the configurable features:
Minimum and maximum password lengths
Force a mix of digits, lowercase, uppercase, symbols, and non-ASCII characters
Minimum number of words in a passphrase
Minimum number of characters to consider as a string (dictionary word)
Ability to search for strings that are words written backwards, or are words written in a mix of upper- and lowercase
Check new password for similar string contained within old password
Suggest a randomly generated password
Setting to either warn about weak passwords or enforce strong passwords
How many times a user is allowed to retry setting a password if he fails to choose a strong password
Once you’ve finished perusing the manpage, you should have a
list of values that you’ll want to modify to reflect your network’s
security policy. Enabling pam_passwdqc
is simply a matter of adding or editing a line so that
it contains your customized options.
On FreeBSD 4.x, add that line to the password
section of /etc/pam.conf. On 5.x, edit instead the
password
section of /etc/pam.d/passwd. Let’s look at that file
on a FreeBSD 5.1 system:
# more /etc/pam.d/passwd
# $FreeBSD: src/etc/pam.d/passwd,v 1.1 2002/04/15 03:01:31 des Exp $
# PAM configuration for the "passwd" service
# passwd(1) does not use the auth, account or session services.
# password
#password requisite pam_passwdqc.so enforce=users
password required pam_unix.so no_warn try_first_pass
Obviously, you’ll need to uncomment the pam_passwdqc.so
line to enable the module.
Note the one included option, enforce=users
, overrides the default setting
of enforce=everyone
.
Let’s see what happens when I remove that remark and then try to
use passwd
as a regular user named
test
. Even though passwords aren’t
echoed to the terminal, I’ve shown in this output the passwords that I
typed in:
%passwd
Changing local password for test Old Password:test
You can now choose the new password or passphrase. A valid password should be a mix of upper and lower case letters, digits and other characters. You can use an 8 character long password with characters from at least 3 of these 4 classes, or a 7 character long password containing characters from all the classes. Characters that form a common pattern are discarded by the check. A passphrase should be of at least 3 words, 12 to 40 characters long and contain enough different characters. Alternatively, if noone else can see your terminal now, you can pick this as your password: "inward!smell:Milan".
As you can see, the password policy is provided, along with an
example of a strong password that meets the policy requirements.
Except for that one option, this particular policy includes the
default settings mentioned in man
pam_passwdqc
.
Enter new password: test
Weak password: is the same as the old one.
Try again.
Here I tried to use the same password. Even worse, it doesn’t
meet any of the password policy’s requirements. However, pam_passwdqc
rejected the password, gave me
another try, and patiently repeated the password policy along with
another password suggestion:
You can now choose the new password or passphrase.
A valid password should be a mix of upper and lower case letters,
digits and other characters. You can use an 8 character long
password with characters from at least 3 of these 4 classes, or
a 7 character long password containing characters from all the
classes. Characters that form a common pattern are discarded by
the check.
A passphrase should be of at least 3 words, 12 to 40 characters
long and contain enough different characters.
Alternatively, if noone else can see your terminal now, you can
pick this as your password: "Sony,seed,cereal".
Enter new password: test1
Weak password: too short.
Try again.
Well, I tried another variation of my old password, but it is still too short. Here we go again:
You can now choose the new password or passphrase.
A valid password should be a mix of upper and lower case letters,
digits and other characters. You can use an 8 character long
password with characters from at least 3 of these 4 classes, or
a 7 character long password containing characters from all the
classes. Characters that form a common pattern are discarded by
the check.
A passphrase should be of at least 3 words, 12 to 40 characters
long and contain enough different characters.
Alternatively, if noone else can see your terminal now, you can
pick this as your password: "torso&lotus_burly".
Enter new password: test1234
Weak password: not enough different characters or classes for this length.
passwd: pam_chauthtok( ): authentication token failure
%
Looks like the default retry count is three, as I was booted out after three tries. This time the password was long enough at eight characters, but only contained numbers and lowercase characters. The instructions clearly state that an eight-character password needs a mix of three different types of characters.
It’s important to note that if the superuser changes a user’s
password, she will receive the same error messages if the password
does not comply with the policy. However, after the error message, the
superuser will be asked to retype that poor password and it will be
accepted. Why? Because of that enforce=users
option. If you remove that
option, it will default back to enforce=everyone
, which requires even the
superuser to choose good passwords. The method you choose will depend
upon the security requirements of your password policy.
It’s easy to change the default settings. Simply add your option to the end of the pam_passwdqc.so line. Then, test your change as a regular user to see what effect it has. You may want to create a test account for just this purpose.
For example, to force users to choose a password that is 10 characters
long and a mix of uppercase letters, lowercase letters, numbers, and
symbols, set N4
to 10
and disable the other options. Don’t know
what N4
is? Better reread that
section of the manpage before changing this parameter.
password requisite pam_passwdqc.so min=disabled,disabled,disabled,disabled,10
Or, to force users to use the randomly picked password:
password requisite pam_passwdqc.so random=42,only
Here I’ve used the default random value of 42
. You can experiment by increasing that
number until the randomly generated passwords meet your strength
requirements. Settings much higher than 70
may produce error messages; this is what
the end user will see:
System configuration error. Please contact your administrator. passwd: pam_chauthtok(1): authentication token failure
The superuser will see:
This system is configured to use randomly generated passwords only, but the attempt to generate a password has failed. This could happen for a number of reasons: you could have requested an impossible password length, or the access to kernel random number pool could have failed. passwd: pam_chauthtok(1): authentication token failure
That’s your hint to choose a lower random number.
Once you’ve settled on a reasonable number, this is what users will see when they change their passwords:
% passwd
Changing local password for test
Old Password:
You can now choose the new password.
This system is configured to permit randomly generated passwords
only. If noone else can see your terminal now, you can pick this
as your password: "lounge-mummy:cellar-dozen". Otherwise, come back later.
Enter new password:
A user who hates that password can retry a few times to see other possibilities. Pressing Enter will generate another random password. Typing in anything other than the randomly generated password will cause the password change to fail.
You may have noticed that pam_passwdqc
does not control how often a
user is forced to change his password. Set this instead in /etc/login.conf. Besides the actual expiry
period, you can also change the amount of advance warning users will
receive about an impending password change.
If you make any changes to /etc/login.conf, test your changes by immediately logging in at another terminal. A typo in this file can prevent logins to a system!
For example, adding these lines to the default:
section will set a password expiry
of 30 days, giving 5 days warning:
:warnpassword=5d: :passwordtime=30d:
If one of those entries happens to be the final entry in the
default:
section, don’t include
the trailing in that last
entry.
Don’t forget to rebuild the database once you’ve saved your changes:
# cap_mkdb /etc/login.conf
man pam_passwdqc
man login.conf
The Pluggable Password Checking web site (http://www.openwall.com/passwdqc/README.shtml)
The PAM Essentials section of the FreeBSD Handbook (http://www.freebsd.org/doc/en_US.ISO8859-1/articles/pam/index.html)
Make it easier for your users to choose good passwords.
It doesn’t matter whether you’re an administrator responsible for enforcing a password policy or an end user trying to comply with said policy. You’re struggling against human nature when you ask users to choose—and remember—hard-to-guess passwords. Passwords that aren’t random are easy to guess, and passwords that are too random tend to manifest themselves on sticky notes under users’ keyboards or in their top drawers.
Wouldn’t it be great if you could somehow offer users random but memorable password choices? There’s a standard designed for just this purpose: APG, the Automated Password Generator.
If you’re running FreeBSD, you can install apg
from the
ports collection:
#cd /usr/ports/security/apg
#make install clean
Once the port is installed, any user can run apg
to generate a list of random, but
pronounceable and memorable, passwords:
% apg -q -m 10 -x 10 -M NC -n 10
plerOcGot5 (pler-Oc-Got-FIVE)
fobEbpigh6 (fob-Eb-pigh-SIX)
Ekjigyerj7 (Ek-jig-yerj-SEVEN)
CaujIvOwk8 (Cauj-Iv-Owk-EIGHT)
yenViapag0 (yen-Viap-ag-ZERO)
Fiwioshev3 (Fi-wi-osh-ev-THREE)
Twomitvac4 (Twom-it-vac-FOUR)
varbidCyd2 (varb-id-Cyd-TWO)
KlepezHap0 (Klep-ez-Hap-ZERO)
Naccudhav8 (Nac-cud-hav-EIGHT)
Notice that each password comes with a pronunciation guide, since it’s easier to remember something you can pronounce.
Also, note that syntax. We’re definitely going to have to do something about all of those switches! But first, let’s take a look at Section 3.2 and make sure we understand them.
Option | Explanation |
| Suppresses warnings (think |
| Sets the minimum password length to 10 characters |
| Sets the maximum password length to 10 characters |
| Requires numerals and capitals |
| Generates 10 password choices |
While this utility is very handy, we can definitely hack in our own improvements. For starters, users aren’t going to use a utility that requires a line’s worth of switches. Second, we don’t want to install this utility on every system in our network. Instead, let’s work out a CGI script. That way users can access the script from their web browsers.
First, let’s sort out all of the switches we’ll use in the
script. We need something to add a punctuation character in the
middle, or we won’t meet Air Force password regulations. The simplest
fix is to run apg
twice with
smaller password requirements, concatenating the results. The first
run, without punctuation characters, looks like this:
% apg -q -m 4 -x 4 -M NC -E Ol -n 10
Dij6 (Dij-SIX)
Voj6 (Voj-SIX)
Pam0 (Pam-ZERO)
Dev9 (Dev-NINE)
Non6 (Non-SIX)
Eyd7 (Eyd-SEVEN)
Vig9 (Vig-NINE)
Not8 (Not-EIGHT)
Nog2 (Nog-TWO)
Von9 (Von-NINE)
Here I’ve reduced the minimum and maximum password length to
four characters. I’ve also added the option -E Ol
to exclude capital “oh” and small
“ell” from passwords, because they’re easily confused with the digits
zero and one.
The second run includes the -S
option, which makes the password
generator use special characters:
% apg -q -m 4 -x 4 -M S -E Ol -n 10
orc) (orc-RIGHT_PARENTHESIS)
tof| (tof-VERTICAL_BAR)
fed^ (fed-CIRCUMFLEX)
gos@ (gos-AT_SIGN)
sig& (sig-AMPERSAND)
eif) (eif-RIGHT_PARENTHESIS)
eds{ (eds-LEFT_BRACE)
lek> (lek-GREATER_THAN)
tij: (tij-COLON)
rot] (rot-RIGHT_BRACKET)
Now for a CGI script to paste the results together. I’ve numbered each line of the script for explanation purposes. Don’t include line numbers when you create your own script.
This script is written in the Korn shell, but can be modified for any shell. To run as is, install the Korn shell from /usr/ports/shells/ksh93.
1 #!/bin/ksh 2 # run apg twice, concatenate results. 3 # exclude most special characters requiring shift key, 4 # capital "oh" (looks like zero), 5 # lowercase "ell" (looks like digit "one") 6 PATH=/bin:/usr/bin:/usr/local/bin; export PATH 7 umask 077 8 a=/tmp/apg.$RANDOM 9 b=/tmp/apg.$RANDOM 10 cat << EOF 11 Content-type: text/html 12 <!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN"> 13 <html> 14 <head> 15 <title>Help generating a new password</title> 16 </head> 17 <body> 18 <h3>Help generating a new password</h3> 19 <blockquote> 20 These passwords should be reasonably safe. 21 Feel free to use one, or reload the page 22 for a new batch.</p> 23 <blockquote> <pre> <font size="+1"> 24 EOF 25 apg -q -m 4 -x 4 -M NC -E '!@#$%^&*( )' -n 10 > $a 26 apg -q -m 4 -x 4 -M S -E '!@#$%^&*( )' -n 10 > $b 27 # tr command is for bug workaround; apg is not supposed to 28 # include characters specified after -E option. 29 paste $a $b | 30 tr 'l' 'L' | 31 awk ' 32 BEGIN { 33 printf "Password Rough guess at pronunciation <hr />" 34 } 35 { 36 printf "%s%s %s %s ", $1, $3, $2, $4 37 }' 38 cat << EOF 39 </font> 40 </pre> 41 </blockquote> 42 </blockquote> 43 <hr /> 44 </body> 45 </html> 46 EOF 47 rm $a $b 48 exit 0
Line 6 sets the PATH
to a
known safe value. This lessens the possibility that an attacker can
cause this program to execute a hazardous binary. Make sure apg
is in this path.
Line 7 sets the umask so that only this user can read the temporary files to be generated later.
Lines 8 and 9 work because Korn shell scripts generate random
numbers automatically. If /bin/ksh is not on your system, use
mktemp
to generate temporary files safely.
Lines 10-24 print the page header. I usually make a sample page
and then run it through /usr/ports/www/tidy to get a decent
DOCTYPE
header and
indentation.
Lines 25 and 26 issue apg
commands to generate two separate files containing four-character
passwords.
Lines 31-37 use an awk
script
to print the password plus its pronunciation. The BEGIN
section prints only once, before any
lines are read. The printf
section
expects lines with four fields: two pairs of password and
pronunciation strings from the temporary files. The first and third
fields are printed together to form the password, and the second and
fourth fields are printed together to form the pronunciation
guess.
Lines 38-46 finish the page.
Lines 47 and 48 clean up the temporary files.
man apg
man mktemp
The APG web site (http://www.adel.nursat.kz/apg/)
FIPS 181, the APG Standard (http://www.itl.nist.gov/fipspubs/fip181.htm)
Sometimes even a complex password may not meet your security needs.
If you are on the road and need to access the corporate network from a non-secure computer in a public place, the risk of password leakage increases. Could the person next to you be shoulder surfing, watching as you log into the network? Does the computer you’re using have some sort of installed spyware or keystroke logger? Is there a packet sniffer running somewhere on the network? In such a situation, a One Time Password can be a real lifesaver.
FreeBSD comes with OPIE, or One-time Passwords In Everything, a type of software OTP system. It is easy to configure and doesn’t require any additional hardware or proprietary software running on a server. Ideally, you should configure OPIE before leaving your secure network. For example, if you plan on traveling with your laptop, configure OPIE while connected to the office network. Make sure you are logged in as your regular user account to the particular system you’ll need to access while on the road.
Start by adding yourself to the OPIE database, or /etc/opiekeys, using opiepasswd
. If you
intend to access your workstation while on the road, run this command
while physically sitting at your workstation. Include the console
switch (-c
) to indicate you are at
that station’s console, so it is safe to enter a passphrase:
% opiepasswd -c
Adding dru:
Only use this method from the console; NEVER from remote. If you are using
telnet, xterm, or a dial-in, type ^C now or exit with no password.
Then run opiepasswd without the -c parameter.
Using MD5 to compute responses.
Enter new secret pass phrase:
Secret pass phrases must be between 10 and 127 characters long.
Enter new secret pass phrase:
Again new secret pass phrase:
ID dru OTP key is 499 dh0391
CHUG ROSA HIRE MALT DEBT EBEN
See that warning at the beginning? If you don’t have physical
access to the system’s keyboard—say, you’re logging into a server—make
sure you use ssh
to log into that
system before running the opiepasswd
-c
command. Your only protection from
another user using your one-time password is your passphrase, which is basically a long password that can
include spaces. If that passphrase is transmitted over the network in
clear text, you’ve defeated the whole purpose of this exercise.
Note that the passphrase isn’t used as a password per se; instead, it is used to prove who added the account to the database and is therefore the rightful owner of the resulting response or one-time password. You’ll need to issue that passphrase whenever you need to view your responses. Responses are always comprised of six uppercase nonsense words.
Next, verify that you are indeed in the OPIE database:
% opieinfo
498 dh0391
The opieinfo
command displays
the count (498
) that will be used
at the next login. It will also display the seed associated with that
count (dh0391
). In this example, it
is expecting the response associated with 498
, but I only know the response for
499
. I’ll need to use an OTP password calculator to figure out the correct
response; that calculator is really just the opiekey
command.
You could use the calculator from a separate terminal every time you login, but it is usually more convenient to print a list of responses and regenerate a new list whenever you run out of responses.
In order to use the calculator, you need to know three things:
Your current counter
Your seed
Your secret passphrase
The challenge at the login prompt will display the current counter and seed. However, it is important that only you know your secret passphrase. Otherwise, anyone could calculate the response and log into your account.
To generate a list of responses, use the number switch (-n
), followed by the number of desired
responses and your current count and seed:
% opiekey -n 5 498 dh0391
Using the MD5 algorithm to compute response.
Reminder: Don't use opiekey from telnet or dial-in sessions.
Enter secret pass phrase:
494: MEAN ADD NEON CAIN LION LAUD
495: LYLE HOLD HIGH HOME ITEM MEL
496: WICK BALI MAY EGO INK TOOK
497: RENT ARM WARN ARC LICE DOME
498: LEAD JAG MUCH MADE NONE WRIT
You can either direct that output to a printer or record those responses by hand. Either way, store those responses in a safe place such as your wallet, as these are your next five one-time passwords. The next time you log in, use the response that matches the count at your login prompt:
login:dru
otp-md5 498 dh0391 ext Password: (here I pressed Enter) otp-md5 498 dh0391 ext Password [echo on]:LEAD JAG MUCH MADE NONE WRIT
Once you configure OPIE on a 5.1 FreeBSD system, you will be required to respond to the OTP challenge whenever you log into that system. If you press Enter, you’ll turn on echo so you can see the response as you type it.
Echo is usually a bad thing when logging in because anyone can see your password. However, with a one-time password, it doesn’t matter if anyone sees that password, as it can’t be reused. Also, unlike a reusable password, the response is not case-sensitive, so it doesn’t matter if you type it in upper- or lowercase. Do take care, though, that no one sees your list of responses or your passphrase.
If your counter gets low—say, 10 or less—reset it before it hits
0. Use opiepasswd
again,
but this time specify a new count and a new seed. Here I’ll use a
count of 499
and a new seed of
dh1357
:
%opiepasswd -n 499 -s dh1357
Updating dru: You need the response from an OTP generator. Old secret pass phrase: otp-md5 8 dh0391 ext Response:loot omit safe eric jolt dark
New secret pass phrase: otp-md5 499 dh1357 Response:hewn as dot mel mali mann
How long it will take you to cycle through your OTP passwords will depend upon how often you need to log in. You may find it convenient to generate a week’s worth of responses at the beginning of each week.
It’s also a good idea to consider how often to change your
passphrase. You’ll be prompted to when you reset your
counter. For example, if you plan on changing your passphrase every
100 responses, specify -n 100
when
you run opiepasswd
. The passphrase
itself needs to be memorable. Fortunately, it can contain spaces, so
you can input, say, a line from a song or a poem.
Starting with FreeBSD 5.1-RELEASE, users are forced to use OTP
once they’ve added themselves to the OPIE database. It doesn’t matter
if the user logs into that system using a local keyboard or over the
network using ssh
. This behavior is
controlled by PAM, or, to be more specific, the auth
section of /etc/pam.d/login:
% more /etc/pam.d/login
#
# $FreeBSD: src/etc/pam.d/login,v 1.11 2002/05/08 00:33:02 des Exp $
#
# PAM configuration for the "login" service
#
# auth
auth required pam_nologin.so no_warn
auth sufficient pam_self.so no_warn
auth sufficient pam_opie.so no_warn no_fake_prompts
auth requisite pam_opieaccess.so no_warn
#auth sufficient pam_kerberosIV.so no_warn try_first_pass
#auth sufficient pam_krb5.so no_warn try_first_pass
#auth sufficient pam_ssh.so no_warn try_first_pass
auth required pam_unix.so no_warn try_first_pass nullok
snip
Perhaps you’d like users to have the option of using their
regular password when logging in locally, but force them to use OTP
when logging in over the network. To achieve that, add the allow_local
option to the opieaccess
line so it looks like
this:
auth requisite pam_opieaccess.so allow_local no_warn
This option lets the user type either her regular password or her OTP response if she’s logging in locally. However, if she’s logging in over the network, the login attempt will fail unless she gives the correct OTP response.
In this chapter, we’ve covered many methods of securing the boot and login environments. It’s probably no surprise that you can further control who can log into your system and when: Unix systems contain many built-in mechanisms, allowing you to choose the most appropriate means and policy for your network.
Furthermore, the defaults may not always suit your needs. Do you really want users to be logged into multiple terminals when they can effectively do their work from one? For that matter, do you want any user, including nonemployees, to try his hand at logging into your systems at any hour of the night and day? Here’s how to tighten up some defaults.
Since users log into terminals, a logical file to secure is the terminal configuration file, /etc/ttys. We briefly saw this file in [Hack #25] when we password protected single-user mode.
This file is divided into three sections, one for each of the
three types of terminals. Let’s concern ourselves with the virtual
terminals, ttyv
, which are the
terminals available for users physically seated at the system’s
keyboard.
# grep ttyv /etc/ttys
ttyv0 "/usr/libexec/getty Pc" cons25 on secure
ttyv1 "/usr/libexec/getty Pc" cons25 on secure
ttyv2 "/usr/libexec/getty Pc" cons25 on secure
ttyv3 "/usr/libexec/getty Pc" cons25 on secure
ttyv4 "/usr/libexec/getty Pc" cons25 on secure
ttyv5 "/usr/libexec/getty Pc" cons25 on secure
ttyv6 "/usr/libexec/getty Pc" cons25 on secure
ttyv7 "/usr/libexec/getty Pc" cons25 on secure
ttyv8 "/usr/X11R6/bin/xdm -nodaemon" xterm off secure
The word on
indicates that
that terminal is available for logins. By default, the first eight
terminals, ttyv0
through ttyv7
, will accept logins. You’ve probably
discovered this yourself by pressing Alt-Fx,
where x is a number between 1 and 8. On a server
system, you may need only one virtual terminal. Disable the other
terminals by changing the word on
to off
.
If the system is running headless [Hack #26] , disable all of the virtual terminals only after you’ve ensured that you have an alternate way to access the system.
The word secure
means that
the system is physically secure, implying that it’s okay for a user to
walk up to the keyboard and log in as root. Since it’s
never okay for a user to log in as root, you
should disable that default. For whatever virtual terminals you’ve
left on
, either change the word
secure
to insecure
or simply remove the word secure
.
Now let’s see what can be done with /etc/login.access. At its most stringent, you can use this file to prevent all remote logins, meaning you can log in only if you are physically sitting at that system:
-:ALL:ALL EXCEPT LOCAL
Note the syntax that is used for each line in this file. The
-
means access denied. Its alter
ego is +
, which means access
granted. The first ALL
is a
wildcard for all users. The second ALL
is a wildcard for all locations. The
EXCEPT
LOCAL
is the exception that allows just the
local location.
You could modify that rule slightly to disallow remote and local root logins:
-:root:ALL
Take some care when modifying this file. Its syntax supports both user and group names, allowing you to specify exactly who is allowed to log into a system. This can be extremely useful in limiting access to a server system.
The syntax also supports IP addresses. This can also be useful in ensuring that only hosts in your network or a particular subnet can access certain systems. But, as in any security mechanism that relies on IP addresses, do keep in mind that IP addresses can be spoofed.
Finally, if you make changes to this file, test your changes immediately. If you restrict access to certain users, ensure those users can still log in. Further, try to log in as other users to ensure that they are actually being denied access.
Think for a moment. Other than logins to virtual terminals, how
else do your users log into systems? Most likely (and, hopefully)
through ssh
. You can control
exactly who can ssh
into a system
by adding a line to the /etc/ssh/sshd_config file of the system
running the SSH daemon.
There are two ways you can control this. One is through AllowGroups
. By default, all groups—meaning
all users—can ssh
into a system.
The other way is through AllowUsers
, where again, all users are
allowed by default.
Suppose I want to allow only the users genisis
, biko
, and dru
to ssh
into a particular system. I could create
a group called remote
that contains
those users:
#grep 100 /etc/group
# #pw groupadd remote -g 100 -M genisis biko dru
In this example, I first double-checked that the group ID of 100
was not currently in use. I then created, with pw groupadd
, the remote
group with a GID of 100 (-g 100
) and with those three members
(-M genisis biko dru
).
Now I can limit ssh
access to
just the members of that group:
# echo 'AllowGroups remote' >> /etc/ssh/sshd_config
Alternatively, I could have just added those three users directly:
# echo 'AllowUsers genisis biko dru' >> /etc/ssh/sshd_config
Any user who does not match either AllowGroups
or AllowUsers
will still receive a password
prompt when attempting to connect to the SSH daemon. However, the
connection attempt will fail with a permission denied
message, even if the user
provides a correct username and password. The SSH daemon will print a
message regarding the failed attempt to its console, sending a copy to
/var/log/messages and emailing to
root
as part of the daily security
run output.
To be even pickier, if your users always log in from the same system, you can do this:
AllowUsers [email protected] [email protected] [email protected]
However, don’t be that picky if your users don’t have static IPs!
Remember, if you make any changes to the SSH daemon’s
configuration file, you’ll need to send a “signal one” to sshd
to notify it of the changes:
# killall -1 sshd
After informing sshd
of the
changes, immediately use a ssh
client to test your changes. For
example, if I instead add the line Allowusers
genisis biko
dru
, I’ll
find that user nastygirl
is still
able to connect. Why? The parameters in /etc/ssh/sshd_config are
case-sensitive. You don’t want to find out six
months later that anyone was allowed to connect when you thought you
had restricted connections to certain users.
We’ve restricted who can log in and from where for both local
and remote ssh
logins, but we still
haven’t restricted when those users can log in.
To do that, let’s look at some other options that are available in our
old friend /etc/login.conf
[Hack #29] .
This file supports the options times.allow
and times.deny
. For
example, to allow all users to log in between 9:00 AM and 5:00 PM
every Monday through Friday, add this line to the default:
section:
:times.allow=Mo-Fr0900-1700:
Once you introduce the times.allow
option, access will
automatically be denied for the time period not
listed.
The converse also works. That is, you can specify the denied
times in times.deny
, and all other
times will be allowed.
Remember, whenever you make a change to /etc/login.conf, rebuild the database with
cap_mkdb /etc/login.conf
and test
your changes.
man ttys
man login.access
man sshd_config
man login.conf
[1] CMOS is battery-powered memory that holds system settings such as the time, date, and system configuration.