Establishing Good Password Policies

01/17/2001

In the last few articles, we've looked at creating users and user policies. In this week's article, I'd like to take a look at how to create a password policy on your FreeBSD system.

In order for any user to log in to a FreeBSD system, they need to have a previously created user account and know the password associated with that user account. One of the responsibilities of the system administrator is to create a password policy that is appropriate for the users of the network. When creating the password policy, you need to consider the following points:

What is the minimum required length of passwords? For example, are blank passwords (a bad idea) allowed, or do passwords have to be at least so many characters in length?

What characters are allowed in a password? That is, can a password contain all lowercase letters, or must they contain some combination of upper and lowercase letters, numbers, and symbols?

What is the password expiration date, meaning, how often are users forced to change their password?

Do you want the system to enforce password history? This means that when a user changes their password, they can't change it back to their old password or a password they've used before. This is also sometimes called password uniqueness.

Do you want to enforce lockout, and if so, after how many bad password attempts? Lockout means that a user will no longer receive the login prompt if they mistype the password so many times during a login attempt.

Often, administrators will have a password policy for regular user accounts and a separate policy for the root user account. For example, it is common to have a password length of 6 characters for regular users, but require a password length of 11 characters for the root user account. You may decide that it is too difficult to force users to use a password that requires both uppercase characters and symbols, but may want to keep this as a requirement on the root password so it will be much more difficult to guess.

There are additional considerations when creating a password policy. When a user account is created, the password is also created by the administrator. It is recommended that users immediately change this password the first time they log in; this ensures that no one knows the user's password except that user. Users should be taught not to give their password to anyone for any reason; remember, if worst comes to worst and a user forgets their password or a user leaves and access to their resources is required, the superuser has the ability to change the user's password.

Since users are responsible for creating their own passwords, it is up to the
system administrator to educate users on what does and does not constitute a
good password. Being human, it is far easier to remember a password that is the
same as my username, my real name, my nickname, my dog's name, etc.
Unfortunately, these are all examples of bad passwords. Many articles have been
written that give examples of both good and bad passwords and the reasons why
creating a good password is important. Here is one such article.

Let's assume we wish to implement the following example password policy:

Regular users have a minimum password length of 8 characters.

The root user has a minimum password length of 11 characters.

All users must change their password every 30 days.

Users must have at least one non-letter character in their password.

Lockout occurs after 5 bad login attempts.

Password history does not allow a user to reuse any of his last 10 passwords.

Users are not allowed to use their username as their password.

Some of these measures can be implemented by editing the /etc/login.conf file, while others may require the installation of a utility or the use of a script. Let's start by seeing which parts of this policy we can implement with the /etc/login.conf file. Since this is a new file to us, let's gather some information about it:

It looks like this file is in C++ program text, and only the superuser can make changes to it.

whatis login.conf
login.conf(5) - login class capability database

As you can see, /etc/login.conf is a database; as such, it has some pretty picky syntax concerning the entries it contains. Also, since it is a database, it is not enough for the superuser to just edit this file with an editor. If any changes are made to this file, the database must be updated using the cap_mkdb utility:

whatis cap_mkdb
cap_mkdb(1) - create capability database

Let's take a look at some of the syntax this database uses. Because it is a class database, each record contains the information for one class. The bits of information are called fields, every field contains a value, and all the fields are separated by colons. To make the file a bit easier to read and edit, each field is on its own indented line; however, since all the fields are logically lumped together for each record, a slash is used to indicate that there is more information regarding the record on the next line. So, the syntax of each record ends up looking like this:

classname:\
:field_1=value:\
:field_2=value:\
:last_field=value:

Note that every field is enclosed by two colons, and the last field does not have a trailing slash, as it is the last entry for that record.

Your FreeBSD system comes with several predefined classes, and each of these classes has predefined fields in the /etc/login.conf database. The first class, default, is used by all users unless you assigned them to a different class when you created their user account. (See last week's article.) The second class, root, is used by all user accounts that have a UID of 0.

The other predefined classes are standard, xuser, staff, daemon, news, and dialer. Each of these records has one field that is the same for each of these records. Let's look at the record for xuser as an example:

ls /etc/login.conf

<snipped output to show only the lines we're interested in>

xuser:\
:tc=default:

Notice that since there is only one field, it does not end with a slash. The tc= tells the database to substitute the fields found in the default class. That is, by default, if you put a user in the "xuser" class, they will receive all the same information as a user in the "default" class. If you are going to put users into different classes, you will want to first change the information available to each class.

So, what type of information belongs in the fields? There are many predefined fields and they have been divided into the four major functions provided by this database: resource limits, environment, authentication, and accounting limits. The following table shows the possible fields:

Name

Type

Notes/Description

Resource Limits

cputime

time

CPU usage limit.

filesize

size

Maximum file size limit.

datasize

size

Maximum data size limit.

stacksize

size

Maximum stack size limit.

coredumpsize

size

Maximum coredump size limit.

memoryuse

size

Max. size limit of core memory use.

memorylocked

size

Maximum locked-in core memory size limit.

maxproc

number

Maximum number of processes.

openfiles

number

Maximum number of open files per process.

sbsize

size

Maximum permitted socketbuffer size.

Environment

charset

string

Set $MM_CHARSET environment variable to the specified value.

hushlogin

bool (false)

Same as having a ~/.hushlogin file.

ignorenologin

bool (false)

Login not prevented by nologin.

lang

string

Set $LANG environment variable to the specified value.

manpath

path

Default search path for manpages.

nologin

file

If the file exists, it will be displayed and the login session will be terminated.

path

path (/bin /usr/bin)

Default search path.

priority

number

Initial priority (nice) level.

requirehome

bool (false)

Require a valid home directory to log in.

setenv

list

A comma-separated list of environment variables and values to which they are to be set.

shell

prog

Session shell to execute rather than the shell specified in the password file. The SHELL environment variable will contain the shell specified in the password file.

term

string

Default terminal type if not able to determine from other means.

timezone

string

Default value of $TZ environment variable.

umask

number (022)

Initial umask. Should always have a leading 0 to ensure octal interpretation.

welcome

file (/etc/motd)

File containing welcome message.

Authentication

minpasswordlen

number (6)

The minimum length a local password may be.

passwd_format

string (md5)

The encryption format that new or changed passwords will use. Valid values include "md5" and "des." NIS clients using a non-FreeBSD NIS server should probably use "des."

mixpasswordcase

bool (true)

Whether passwd(1) will warn the user if an all-lowercase password is entered.

copyright

file

File containing additional copyright information

host.allow

list

List of remote host wildcards that users in the class may access.

host.deny

list

List of remote host wildcards that users in the class may not access.

times.allow

list

List of time periods during which logins are allowed.

times.deny

list

List of time periods during which logins are disallowed.

ttys.allow

list

List of ttys and ttygroups that users in the class may use for access.

ttys.deny

list

List of ttys and ttygroups that users in the class may not use for access.

Accounting Limits

accounted

bool (false)

Enable session-time accounting for all users in this class.

autodelete

time

Time after expiry when account is auto-deleted.

bootfull

bool (false)

Enable "boot only if ttygroup is full" strategy when terminating sessions.

daytime

time

Maximum login time per day.

expireperiod

time

Time for expiry allocation.

graceexpire

time

Grace days for expired account.

gracetime

time

Additional grace login time allowed.

host.accounted

list

List of remote host wildcards from which login sessions will be accounted.

host.exempt

list

List of remote host wildcards from which login session accounting is exempted.

idletime

time

Maximum idle time before logout.

monthtime

time

Maximum login time per month.

passwordtime

time

Used by passwd(1) to set next password expiry date.

refreshtime

time

New time allowed on account refresh.

refreshperiod

str

How often account time is refreshed.

sessiontime

time

Maximum login time per session.

sessionlimit

number

Maximum number of concurrent login sessions on ttys in any group.

ttys.accounted

list

List of ttys and ttygroups for which login accounting is active.

ttys.exempt

list

List of ttys and ttygroups for which login accounting is exempt.

warnexpire

time

Advance notice for pending account expiry.

warnpassword

time

Advance notice for pending password expiry.

warntime

time

Advance notice for pending out-of-time.

weektime

time

Maximum login time per week.

Two of these values will be useful to us in implementing our password policy: minpasswordlen and passwordtime. Since our policy has a different password length for regular users and the superuser, we'll have to edit the records of both "default" and "root." Let's first take a look at the default records of these two classes:

Notice that the root user contains that tc= entry, meaning it gets all the default stuff without you having to type it all in again. The only difference is that "root" has been given the ignorenologin value. If we read the description of that value in the table, it means that the root login can't be prevented by a nologin file, so root can always log in.

Now, become the superuser and, using your favourite text editor, add the following lines to /etc/login.conf like so:

Under the default record, put a \ at the end of the umask line and then add two more lines so it looks like this:

:umask=022:\
:minpasswordlen=8:\
:passwordtime=30d:

Notice that we set the minimum password length to 8 and set a password expiration date of 30 days. Now, go down to the root record, and add a line between the ignorelogin line and the tc= line like so:

root:\
:ignorenologin:\
:minpasswordlen=11:\
:tc=default:

Note that we didn't have to repeat the changes we made to the default record. We only had to add an entry for the information that was different, that is, the longer minimum password length for the root user account.

Before saving your file, double-check the syntax of your changes. Once you've saved your file, update the database by running the following command:

cap_mkdb /etc/login.conf

Let's see if our changes worked. I'll log in as the user "genisis" and try to change my password to one with only four characters in it:

If you don't receive the same results, double-check that you don't have a typo in your /etc/login.conf file and that you did remember to remake your database using the cap_mkdb command.

We've covered a lot of material for one day; we'll continue implementing our example password policy in next week's article.

Dru Lavigne
is a network and systems administrator, IT instructor, author and international speaker. She has over a decade of experience administering and teaching Netware, Microsoft, Cisco, Checkpoint, SCO, Solaris, Linux, and BSD systems. A prolific author, she pens the popular FreeBSD Basics column for O'Reilly and is author of BSD Hacks and The Best of FreeBSD Basics.