Tac Plus
Tac Plus
TACACS+
TACACS+ ii
COLLABORATORS
TITLE :
TACACS+
REVISION HISTORY
Contents
1 Introduction 1
1.1 Download . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
3 Operation 2
3.1 Command line syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
3.2 Signals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
3.3 Event mechanism selection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
3.4 Sample Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
3.5 Sample Configuration with Realms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
4 Configuration Syntax 5
4.1 Global options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
4.1.1 Limits and timeouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
4.1.2 DNS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
4.1.3 Process-specific options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
4.1.4 Railroad Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.2 Realms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.2.1 Railroad Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4.3 Realm options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4.3.1 Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4.3.1.1 Accounting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4.3.1.2 Spoofing Syslog Packets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.3.2 Limits and timeouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.3.2.1 DNS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.3.2.2 Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.3.2.3 Miscellaneanous . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.3.2.4 User backend options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.3.2.5 Railroad Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
4.3.3 Hosts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.3.3.1 Timeouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.3.3.2 Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.3.3.3 Banners and Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.3.3.4 Inheritance and Hosts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.3.3.5 Railroad Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.3.3.6 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
TACACS+ iv
5 MAVIS Backends 52
5.1 LDAP Backends . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
5.1.1 LDAP Custom Schema Backend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
5.1.2 Active Directory Backend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
5.1.3 Generic LDAP Backend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
5.2 PAM backend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
5.3 System Password Backends . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
5.4 Shadow Backend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
5.5 RADIUS Backend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
5.5.1 Sample Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
5.6 Experimental Backends . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
5.7 Error Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
6 Debugging 58
6.1 Debugging Configuration Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
6.2 Trace Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
8 Canned Configurations 65
8.1 Login Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
8.2 Command Authorization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
8.3 Network Access Authorization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
8.4 ARAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
8.5 Callback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
9 Authorization AV pairs 69
11 Bugs 78
12 References 78
List of Tables
1 Introduction
tac_plus is a TACACS+ daemon. It provides Cisco Systems routers and access servers with authentication, authorisation and
accounting services.
This version is a major rewrite of the original Cisco source code. Key features include:
• Flexible external backends for user profiles (e.g. via PERL scripts or C; LDAP (including ActiveDirectory), RADIUS and
others are included)
• Connection multiplexing (multiple concurrent NAS clients per process)
• Session multiplexing (multiple concurrent sessions per connection, single-connection)
1.1 Download
The following chapters utilize a couple of terms that may need further explanation:
NAC A Network Access Client, e.g. the source host of a telnet connection.
A Network Access Server, e.g. a Cisco box, or any other client which makes TACACS+
NAS
authentication and authorization requests, or generates TACACS+ accounting packets.
A program which services network requests for authentication and authorization, verifies
Daemon
identities, grants or denies authorizations, and logs accounting records.
Strings of text in the form attribute=value, sent between a NAS and a TACACS+
AV pairs
daemon as part of the TACACS+ protocol.
Since a NAS is sometimes referred to as a server, and a daemon is also often referred to as a server, the term server has been
avoided here in favor of the less ambiguous terms NAS and Daemon.
As a tidbit of historical value, there are about three versions of authentication protocol that people may refer to as TACACS:
The first is ordinary TACACS, which was the first one offered on Cisco boxes and has been in use for many years. The second is
an extension to the first, commonly called Extended TACACS or XTACACS, introduced in 1990. Both are UDP based services.
The third one, and that’s the version virtually everybody uses right now, is TACACS+ or tac_plus, which is what is documented
here. TACACS+ is based on TCP and as such not compatible with any previous versions of TACACS.
Apparently, at least one vendor did implement TACACS+, but refers to it as HWTACACS. Chances are that there are no technical
(but possibly marketing or legal) reasons behind that. Documentation is scarce.
TACACS+ 2 / 79
3 Operation
This section gives a brief and basic overview how to run tac_plus.
In earlier versions, tac_plus wasn’t a standalone program but had to be invoked by spawnd. This has changed, as spawnd func-
tionality is now part of the tac_plus binary. However, using a dedicated spawnd process is still possible and, more importantly,
the spawnd configuration options and documentation remain valid.
tac_plus may use auxiliary MAVIS backend modules for authentication and authorization.
If the program was compiled with CURL support, configuration-file may be an URL.
Keep the -P option in mind - it is imperative that the configuration file supplied is syntactically correct, as the daemon won’t
start if there are any parsing errors at start-up.
The -d switch enables debugging. You most likely don’t want to use this. Read the source if you need to.
The -i option is only honoured if the build-in spawnd functionality is used. In that case, it selects the configuration ID for
tac_plus, while the optional last argument id sets the ID of the spawnd configuration section.
3.2 Signals
Both the master (that’s the process running the spawnd code) and the child processes (running the tac_plus code) intercept the
SIGHUP signal:
• The master process will restart upon reception of SIGHUP, re-reading the configuration file. The child processes will recognize
that the master process is no longer available. It will continue to serve the existing connections and terminate when idle.
• If SIGHUP is sent to a child process it will stop accepting new connections from its master process. It will continue to serve
the existing connections and terminate when idle.
Several level-triggered event mechanisms are supported. By default, the one best suited for your operating system will be used.
However, you may set the environment variable IO_POLL_MECHANISM to select a specific one.
The following event mechanisms are supported (in order of preference):
A single configuration file is sufficient for configuring spawnd, tac_plus and the MAVIS authentication and authorization back-
ends.
The following spawnd configuration stanza accepts connections on TCP ports 49 and 4949 and forwards these to one of the
tac_plus processes. The tac_plus configuration configures a couple of user groups, has one single user defined and relies on the
MAVIS backend for additional users.
#!/usr/local/sbin/tac_plus
id = spawnd {
listen = { port = 49 }
listen = { port = 4949 }
# see the spawnd configuration guide for further configuration options
}
id = tac_plus {
accounting log = /var/log/tac_plus/%Y/%m/%d.log
retire limit = 1000
mavis module = external {
exec = /usr/local/lib/mavis_tacplus_passwd.pl
# see the MAVIS configuration manual for more options and other modules
}
login backend = mavis
host = world {
welcome banner = "\nHitherto shalt thou come, but no further. (Job 38.11)\n\n"
key = QaWsEdRfTgY
enable 15 = clear test
address = 0.0.0.0/0
}
group = readwrite {
default service = permit
service = shell {
default command = permit
set priv-lvl = 15
}
}
group = getconfig {
default service = permit
service = shell {
set autocmd = "sho run"
set priv-lvl = 15
}
}
user = marc {
login = crypt $1$xxxxxxxx$hDZPHghXe8XvoHeFdqUwm/
member = readwrite@world
member = [email protected]/16
}
}
Here’s a more sophisticated example, acknowledging that most folks prefer to look at sample configurations instead of actually
reading documentation.
TACACS+ 4 / 79
id = spawnd {
# Really, have a look at the spawnd configuration. There are
# lots of useful options.
listen = {
port = 4949
}
listen = {
port = 4950 realm = core
}
}
id = tac_plus {
client realm = adminRealm # Could specify these
aaa realm = local # at host level, too.
dns reverse-lookup = no
realm = adminRealm {
host = myPCs {
address = 192.0.2.8,192.0.2.24/29
}
}
realm = customerRealm {
dns reverse-lookup = yes
host = customer {
address = 172.16.0.0/12
}
}
realm = unixRealm {
mavis module = groups {
resolve gids = yes
groups filter = /^(guest|staff)$/
script out = {
# copy the already filtered UNIX group access list to TACMEMBER
eval $GIDS =~ /^(.*)$/
set $TACMEMBER = $1
}
}
TACACS+ 5 / 79
group = staff {
service = shell {
set priv-lvl = 15
script = {
if (cmd == "") permit # shell startup
if (cmd =~ /^configure/) permit
}
message debug = "author ’%c %a’"
cmd = show { permit .* }
}
}
group = guest {
member = staff
service = shell {
cmd = show { permit .* }
message deny = "denied by t+"
}
}
}
realm = ldapRealm {
mavis module = external {
exec = /usr/local/lib/mavis/mavis_tacplus_ads.pl
setenv USE_TLS = 0
setenv LDAP_HOSTS = "192.0.2.193"
setenv LDAP_SCOPE = sub
setenv LDAP_BASE = "dc=example,dc=com"
}
user backend = mavis
login backend = mavis
pap backend = mavis
}
realm = localRealm {
user = marc {
password = clear marc
service = shell {
default command = permit
}
}
}
}
4 Configuration Syntax
The configuration stanzas covered by the following sections are to be placed between
id = tac_plus = {
Including Files
Configuration files may refer to other configuration files:
include =file
will read and parse file. Shell wildcard patterns are expanded by glob(3). The include statement will be accepted virtually
everywhere (but not in comments or textual strings).
1. global definitions
2. hosts
3. timespecs
4. acls
5. groups
6. users
The reasoning behind that non-random order is that parts of the configuration may may use other parts, and these need to exist
before being used.
id = tac_plus
{ GlobalAttr }
TimespecDecl
ACLDecl
RealmDecl
RealmAttr
The global configuration section may contain the following configuration directives, plus the realm options detailed in the next
section. realm options defined at global level will implicitely be assigned to the default realm and will be inherited to subsequently
defined realms.
TACACS+ 7 / 79
A number of global limits and timeouts may be specified exclusively at global level:
• retire limit =n
The particular daemon instance will terminate after processing n requests. The spawnd instance will spawn a new instance if
necessary.
Default: unset
• retire timeout =s
The particular daemon instance will terminate after s seconds. spawnd will spawn a new instance if necessary.
Default: unset
Time units
Appending s, m, h or d to any timeout value will scale the value as expected.
4.1.2 DNS
tac_plus can make use of static and, if compiled with lwres support, of dynamic DNS entries. The relevant global configuration
options at global level are:
host = router.example.com {
# "address = 1.2.3.4" is actually implied
key = mykey
}
• userid =uid
Run daemon under user id uid. This option is deprecated.
• groupid =gid
Run daemon under group id gid. This option is deprecated.
• working directory =directory
Changes the current working directory to directory.
• coredump directory =directory
Dump cores to directory. You really shouldn’t need this.
TACACS+ 8 / 79
umask = umask
syslog facility = string
level
userid = userID
groupid = groupID
working directory = directory
coredump directory = directory
retire limit = count
seconds = seconds
time zone = timeZone
dns cleanup period = seconds
prefetch address CIDR = string
4.2 Realms
Historically, realms were introduced in listen directives in the spawnd configuration section:
spawnd = {
listen = { port = 49 }
listen = { port = 3939 }
listen = { port = 4949 realm = oneRealm}
listen = { port = 5959 realm = otherRealm}
}
Default Realm
If spawnd doesn’t supply a realm name, then tac_plus will fall back to using the realm named default, which contains all
the realm specific directives not defined in realm context.
You can select the actual default realm with
default realm = RealmName
The main task of the spawnd component is to accept a connection and forward it, including the associated realm, to the tac_plus
process. tac_plus then uses that realm to assign the correct host object hierarchy to the connection.
The prefered syntax to use (and define) realms is
realm = realmName { ... }
TACACS+ 9 / 79
at global configuration level. Realms may include hosts, users, groups, MAVIS configurations and various other configuration
options. Realms and hosts may refer to other realms for authentication.
More precisely, the three uses for realms are:
directive.
This may for example be used to enable DNS reverse lookups for remote clients on a per-realm basis.
Defining realms is optional. As mentioned before, realm options defined at top-level will be assigned to the default realm,
which is actually the default for both the aaa and nac realms.
realm = realmName { }
RealmAttr
4.3.1 Logging
• Authentication
authentication log = log_destination
• Authorization
authorization log = log_destination
• Accounting
accounting log = log_destination
TACACS+ 10 / 79
• Files
For logging to plain disk files fcntl(2) file locking is used, so it is recommended that file resides on a local filesystem.
Although fcntl locking over NFS is supported on some Unix implementations, it is notoriously unreliable, and eVen if your
implementation is reliable, locking is likely to be extremely inefficient over NFS.
If the underlying file system supports atomic appends, fcntl(2) file locking may not be necessary. Prepending the log
destination with a > character will turn file locking off and switch log file handling to synchronous mode.
# async
authentication log = /var/log/tac_plus/access1.log
# sync:
authentication log = ">/var/log/tac_plus/access2.log"
• Commands
If the log destination starts with a | character a shell running the remainder of the destination argument will be invoked, and
log messages will be feeded to that shell on standard input. Example:
authentication log = "|exec /usr/bin/logger"
(The exec isn’t strictly necessary here, but avoids keeping an otherwise idle shell process around.)
• Syslog
Logging to syslogd(8) can be enabled by either using the syslog keyword
accounting log = syslog
(which will use your systems syslog(3) function for logging, or by utilizing
accounting log = Address[:UDPPort]
syntax.
Syslog
Logging to syslogd(8) can be disabled using
syslog default = deny
to automate time-based log file switching. By default, the daemon will use your local time zone for time conversion. You can
switch to a different one by using the time zone option (see below).
There are a couple of other configuration options that may be useful:
• umask =mode
This sets the file creation mode mask. Example:
umask = 0640
4.3.1.1 Accounting
All accounting records are written, as text, to the file (or command) specified with the accounting log directive.
Accounting records are text lines containing tab-separated fields. The first 6 fields are always the same. These are:
• timestamp
• NAS address
• username
• port
• NAC address
• record type
TACACS+ 12 / 79
Following these, a variable number of fields are written, depending on the accounting record type. All are of the form attrib
ute=value. There will always be a task_id field.
Current attributes are:
unknown service start_time port elapsed_time status priv_level cmd protocol cmd-arg by
tes_in bytes_out paks_in paks_out address task_id callback-dialstring nocallback-verif
y callback-line callback-rotary
More may be added over time.
Example records (lines wrapped for legibility) are thus:
1995-07-13 13:35:28 -0500 172.16.1.4 chein tty5 198.51.100.141
stop task_id=12028 service=exec port=5 elapsed_time=875
1995-07-13 13:37:04 -0500 172.16.1.4 lol tty18 198.51.100.129
stop task_id=11613 service=exec port=18 elapsed_time=909
1995-07-13 14:09:02 -0500 172.16.1.4 billw tty18 198.51.100.152
start task_id=17150 service=exec port=18
1995-07-13 14:09:02 -0500 172.16.1.4 billw tty18 198.51.100.152
start task_id=17150 service=exec port=18
Elapsed time is in seconds, and is the field most people are usually interested in.
On the NAS, to get accounting records equivalent to previous versions of tacacs, the following is sufficient. "Stop" records
contain elapsed time for connections and exec sessions.
aaa accounting system default start-stop group tacacs+
aaa accounting exec default start-stop group tacacs+
aaa accounting exec acctlist stop-only group tacacs+
aaa accounting commands 15 acctlist start-stop group tacacs+
aaa accounting network acctlist stop-only group tacacs+
line XX
accounting commands 15 acctlist
The script tacspooflog.pl (which comes bundled with the distribution) may be used to make syslogd believe that logs
come straight from your router, not from tac_plus.
E.g., if your syslogd is listening on 127.0.0.1, you may try:
access log = "|exec sudo /path/to/tacspooflog.pl 127.0.0.1"
A number of global limits and timeouts may be specified at realm and global level:
• connection timeout = s
Terminate a connection to a NAS after an idle period of at least s seconds.
Default: 600
• context timeout = s
Clears context cache entries after s seconds of inactivity. Default: 3600 seconds.
Default: 3600
TACACS+ 13 / 79
• warning period = d
Set warning period for password expiry to d days.
Default: 14
4.3.2.1 DNS
tac_plus can make use of static and, if compiled with lwres support, of dynamic DNS entries. The relevant realm configuration
options are:
4.3.2.2 Authentication
Username: admin
Password: ***
Password incorrect.
Username: admin
Password: ****
Password incorrect.
Username: admin
Password: *
Password incorrect.
(the actual default in earlier versions was 4) would change this dialog to:
TACACS+ 14 / 79
Username: admin
Password: ***
Password incorrect.
Password: ****
Password incorrect.
Password: *****
Username:
It’s at the NAS’s discretion to restart the authentication dialog with a new TACACS+ session or to close the (Telnet/SSH/...)
session to the user if TACACS+ authentication fails.
• anonymous-enable = ( permit | deny )
Several broken TACACS+ implementations send no or an invalid username in enable packets. Setting this option to deny
tries to enforce user authentication before enabling. This option defaults to permit.
Alas, this may or may not work. In theory, the enable dialog should look somewhat like:
Router> enable
Username: me
Password: *******
Enable Password: **********
Router#
However, some implementations may resend the user password at the Enable Password: prompt. In that case you’ve got
only two options: Either use
enable = login
at user group level, which will omit the secondary password query and let the user enable with his login password, or permit
anonymous enable (which is disabled by default) with
anonymous-enable = permit
(globally, or at host level) and use enable passwords defined at host level.
• augmented-enable = ( permit | deny )
For TACACS+ client implementations that send $enable$ instead of the real username in an enable request, this will permit
user specific authentication using a concatenation of username and login password, separated with a single space character:
> enable
Password: myusername mypassword
#
enable [ level ] =login needs to be set in the users’ profile for this option to take effect.
Default: augmented-enable =deny
augmented-enable will only take effect if the NAS tries to authenticate a username matching the regex
TACACS+ 15 / 79
^\$enab..\$$
(e.g.: $enable$, $enab15$). That matching criteria may be changed using an ACL:
acl script = custom_enable_acl { if (user =~ ^demo$) permit deny }
enable user acl = custom_enable_acl
4.3.2.3 Miscellaneanous
These options are relevant for configuring the MAVIS user backend:
For non-local users, if the chpass attribute is set and the user provides an empty password at login, the user is given the
option to change his password. This requires appropriate support in the MAVIS backend modules.
• mavis module = module { ... }
Load MAVIS module module. See the MAVIS documentation for configuration guidance.
• mavis noauthcache
Disables password caching for MAVIS modules.
• mavis user filter = [ not ] acl
Query MAVIS user backend only if acl matches. Unset by default. Example:
acl script = validname {
if (user =~ /^[a-z0-9]+$/)
permit
deny
}
MavisDecl
LogDecl
HostDecl
GroupDecl
UserDecl
authentication log = fileName
authorization logName
accounting syslog
connection timeout = seconds
context timeout = seconds
warning period = days
single-connect = yes
no
dns reverse-lookup = yes
no
timeout = seconds
skip conflicting groups = yes
missing no
mavis cache timeout = seconds
date format = string
log separator = string
client realm = realmName
RealmAttrAuthen
Debug
chalresp noecho
chpass
4.3.3 Hosts
The daemon will talk to known NAS addresses only. Connections from unknown addresses will be rejected.
If you want tac_plus to encrypt its packets (and you almost certainly do want this, as there can be usernames and passwords
contained in there), then you’ll have to specify an (non-empty) encryption key. The identical key must also be configured on any
NAS which communicates with tac_plus.
To specify a global key, use a statement similar to
host = 0.0.0.0/0 {
key = "your key here"
}
or, alternatively,
host = world {
key = "your key here"
address = 0.0.0.0/0
}
TACACS+ 19 / 79
(where world is not a keyword, but just some arbitrary character string).
Double Quotes
You only need double quotes on the daemon if your key contains spaces. Confusingly, even if your key does contain spaces,
you should never use double quotes when you configure the matching key on the NAS.
The daemon will reject connections from hosts that have no encryption key defined.
Double quotes within double-quoted strings may be escaped using the backslash character \ (which can be escaped by itself),
e.g.:
key = "quo\\te me\"."
Any CIDR range within a host definition needs to to be unique, and the most specific definition will match. The requirement for
unambiguousness is quite simply based on the fact that certain host object attributes (key, prompt, enable passwords) may only
exist once.
On the NAS, you also need to configure the same key. Do this by issuing:
aaa new-model
tacacs-server host 192.168.0.1 single-connection key your key here
The optional single-connection parameter specifies that multiple sessions may use the same TCP/IP connection to the
server.
Generally, the syntax for host declarations conforms to
host = [ realm realm ] name_or_ip-range { key-value pairs }
The optional realm tells the daemon to use the host definition for connections associated with a corresponding spawnd or
client realm only. For example,
id = spawnd {
listen = { port = 49 }
listen = { port = 4949 realm = space }
}
id = tac_plus {
host = earth { address = ::/0 ... }
host realm space = mars { address = ::/0 ... }
group = admin { ... }
group = alien { ... }
user = marc { member = admin@earth member = alien@mars }
}
makes group membership dependent on the address and/or port the NAS uses to contact the TACACS+ server. User marc will
be a member of group admin for network access servers connecting via port 49, and member of alien for those using port
4949.
In most cases, hosts can be refered to either by IP address or by name.
The key-value pairs permitted in host sections of the configuration file are explained below.
key = ""
Be careful to remember to switch encryption back on again after you’ve finished debugging.
• usage = ( all | any | client | server | group-membership| none )
If set to client or none, TACACS+ session requests that originate from addresses matching the host object will be rejected.
Likewise, if set to server or none, logins that originate from addresses matching the host object will be refused. If group-
membership is set, the host definition may be used for group membership assignments only.
This setting is inherited from a supernet to its subnets and defaults to any.
Realms
You can combine usage with realms to strictly separate client and server host definitions:
# A "clients"-realm that summarizes valid client addresses for authentication:
realm = clients {
host = ::0/0 { usage = none }
host = 172.17.2.0/24 { usage = clients }
}
# ... the "client realm" setting can be used on a per-host basis, too:
host = router { ... client realm = clients ... }
• inherit = ( yes | no )
If set to no, the current host declaration won’t inherit configuration snippets (e.g.: key, enable passwords) from its supernet.
Default is yes.
• name = string
Assigns the name string to the host object. This, combined with the address keyword, may be used to form host groups.
Don’t use this attribute if you’ve already assigned a name in the initial host = statement, and take care to define hosts before
users and groups, or group membership assignments may fail, possibly silently.
• address =cidr
Adds the address range specified by cidr to the current host definition.
internetAddress / maskLen
internetMask
Caveat Emptor
There’s a slight chance that single-connection doesn’t work as expected. The single-connection implementation in your router
or even the one implemented in this daemon (or possibly both) may be buggy. If you’re noticing weird AAA behaviour that
can’t be explained otherwise, then try disabling single-connection on the router.
4.3.3.1 Timeouts
• connection timeout =s
Terminate a connection to this NAS after an idle period of at least s seconds. Defaults to the global option.
4.3.3.2 Authentication
The following authentication related directives are available at host object level:
will tell the daemon not to query for the user name for connections coming from 1.2.3.4, but to use nmsuser instead.
Use this option with care. It may come handy while transitioning to a TACACS+ environment, but has several severe implica-
tions, the first one being that the router has no idea about the username, which implies that authorization via TACACS+ won’t
work.
• default group = name
For users without any group membership this directive may be used to assign one; it’s used for connections originating from
this host object. For example, you can deny access to user without group membership by using something like that:
TACACS+ 22 / 79
Password Hashes
You can use the openssl passwd utility to compute password hashes. For example,
openssl passwd -1 clear_text_password
The time when those texts get displayed largely depends on the actual login method:
Neither the motd banner nor a message defined in the users’ profile will be displayed if hushlogin is set for the user.
Both banners and messages support strftime(3)-style conversion specifications, plus the following conversion sequences:
TACACS+ 23 / 79
Context Directive Login via Telnet Login via SSHv1 Login via SSHv2
displayed before displayed before
host welcome banner not displayed
Username: Password:
displayed before
host reject banner not displayed not displayed
closing connection
displayed after displayed after
host motd banner not displayed
successful login successful login
failed
displayed after displayed after
host authentication not displayed
unsuccessful login unsuccessful login
banner
displayed after displayed after
user or group message not displayed
motd banner motd banner
Example:
host = ... {
...
welcome banner = "Welcome. Today is %A.\n"
...
}
For host lookups, the daemon looks for the most specific host definition first. If that definition (or the requested value) doesn’t
exist, it looks for the next less-specific one. This process continues through the entire (IPv6) CIDR hierarchy, until a match is
found.
In other words: For host parameters, the most specific host definition with that parameter defined matches.
For example, if a host 192.168.1.15 connects to the server, the server needs to know which encryption key to use.
In IPv6-mapped CIDR notation (which the daemon uses internally), the IPv4 CIDR range
192.168.1.15/32
equals
0000:0000:0000:0000:0000:FFFF:C0A8:010F/128
The daemon will search for a key in the host definition for 192.168.1.15 and the (defined) supernets:
0000:0000:0000:0000:0000:FFFF:C0A8:010F/128
0000:0000:0000:0000:0000:FFFF:C0A8:010F/127
0000:0000:0000:0000:0000:FFFF:C0A8:010E/126
0000:0000:0000:0000:0000:FFFF:C0A8:010C/125
...
0000:0000:0000:0000:0000:FFFF:C000:0000/98
0000:0000:0000:0000:0000:FFFF:8000:0000/97
0000:0000:0000:0000:0000:FFFF:0000:0000/96
0000:0000:0000:0000:0000:FFFE:0000:0000/95
0000:0000:0000:0000:0000:FFFC:0000:0000/94
...
0000:0000:0000:0000:0000:0000:0000:0000/3
0000:0000:0000:0000:0000:0000:0000:0000/2
0000:0000:0000:0000:0000:0000:0000:0000/1
0000:0000:0000:0000:0000:0000:0000:0000/0
TACACS+ 24 / 79
template = CIDR
hostName
address = CIDR
name = hostName
usage = any
all
client
server
none
group-membership
inherit = yes
no
dns reverse-lookup = yes
no
dns timeout = seconds
single-connect may-close = yes
no
connection timeout = seconds
aaa realm = realmName
authentication fallback = permit
deny
pap password mapping = login
pap
anonymous-enable = permit
deny
password max-attempts = count
backoff = seconds
default user = userName
default group = groupName
Debug
4.3.3.6 Example
host = 10.0.0.0/8 {
name = customer1
key = "your key here"
welcome banner = "\nHitherto shalt thou come, but no further. (Job 38.11)\n\n"
enable 15 = clear whatever
}
host = test123 {
address = 10.1.2.0/28
address = 10.12.1.30/28
address = 10.1.1.2
# key/banners/enable will be inherited from 10.0.0.0/8 by default,
# unless you specify "inherit = no"
address file = /some/path/test123.cidr
prompt = "\nGo away.\n\n"
}
timespec objects may be used for time based profile assignments. Both cron and Taylor-UUCP syntax are supported; see
you local crontab(5) and/or UUCP man pages for details. Syntax:
timespec = timespec_name { "entry" [ ... ] }
Example:
# Working hours are from Mo-Fr from 9 to 16:59, and
# on Saturdays from 9 to 12:59:
timespec = workinghours {
"* 9-16 * * 1-5" # or: "* 9-16 * * Mon-Fri"
"* 9-12 * * 6" # or: "* 9-12 * * Sat"
}
timespec = example {
Wk2305-0855,Sa,Su2305-1655
Wk0905-2255,Su1705-2255
Any
}
TACACS+ 27 / 79
uucpStyleTime
Access Control Lists (ACLs) consist of a series of Access Control Expressions (ACEs). An ACL does match whenever one of
its ACEs does. The ACEs that form an ACL are processed in sequence. An ACE matches if at least one of each defined subtype
expression type matches. ACEs named identially become part of an ACL with the same name.
Standard ACL syntax is:
acl = name { ( (nac ( = [ not ] (HostName|cidr) ) | ( [ dns ] regex = [ not ] NacRegex )) | (nas ( = [ not ] (HostName|cidr)
) | (regex = [ not ] NasRegex )) | (port regex = [ not ] PortRegex ) | (acl = [ not ] ACLName )* } (time = [ not ]
TimeSpecName )* }
Alternatively, scripting can be used:
acl script = name { ... }
The latter is detailled in the next chapter.
For standard ACL syntax, please have a look at the Railroad Diagrams below, and things will hopefully become much clearer.
ACEs will be evaluated in the order you’ve defined them. E.g.,
acl = myacl {
nac = 10.0.0.0/8
nac = 11.0.0.0/8
nas = somehostobjectname
}
acl = myacl {
time = workinghours
}
consists of two ACEs which form an ACL named "myacl". This ACL matches
When using the dns keyword, NAC regex matching is done against the NACs DNS reverse mapping. This will only work if
a) the software was compiled with lwres support and b) lwresd is actually running. However, keep in mind that DNS isn’t
necessarily trustworthy and lookups might plain fail. Keep in mind that you’re best off enabling single-connection on
your NAS. This allows the daemon to take advantage of caching the reverse-mapping results.
nac == hostName
nas !=
CIDR
dns =~ regEx
!~
nac-name regex = not regEx
nas-name
nac-name =~ regEx
nas-name !~
port regex = not regEx
port =~ regEx
!~
realm regex = not regEx
realm =~ regEx
!~
time = not timeSpecName
time == != timeSpecName
acl == aclName
!=
4.3.6 Scripts
Scripts may currently be used for ACLs and in service declaration scope:
user = joe {
service = shell {
script = {
if (cmd == "") permit # required for shell startup
if (cmd =~ /^(no\s)?shutdown\s/) permit }
}
}
4.3.6.1 Syntax
context = value
message = value
permit
deny
return
{ TacAction }
! TacCond
( TacCond )
( TacCond && )
||
TacCond
cmd == value
context !=
user =~ regEx
port !~
nac == hostName
nas !=
nac =~ regEx
nas !~
nac-name
nas-name
nac-realm
nas-realm
aaa-realm
time == timespecName
!=
acl == aclName
!=
User and group declarations are pretty similar. The major difference is, that groups are static, while user declarations may be
added at run-time via the MAVIS back-end.
A user may be member of a group (also known as role or profile). Actual group membership may depend on various factors, e.g.
the NAS the user is on, the NAC, or time ranges. Each group may in turn be member of another group and so on, ad infinitum.
The basic form of a user and group declarations is
user = username { ... }
or, respectively,
group = groupname { ... }
A user or group declaration may contain key-value pairs and service declarations.
user = marc {
password acl jumpstation {
login = permit
pap = permit
}
password {
login = clear myLoginPassword
pap = clear myPapPassword
}
}
• template = groupname
This directive merges group definitions from groupname to the current group. Already existing definitions take precedence.
The following key-value pairs are valid in both user and group context:
TACACS+ 32 / 79
4.3.7.3.1 Limits
If the regex keyword is used, string is expected to be a valid regular expression. Regular expression search is expensive and
only used if the NAC address sent by the NAS ist not an IP address. Example for regular expression client matching:
# if compiled with PCRE:
client = deny /^async$/
client regex = /^console$/
# without PCRE:
client = deny "^async$"
client regex = "^console$"
Evaluation Order
Please keep in mind that ACLs are checked before client statements.
For client definitions, IP addresses are checked before regular expressions, while the latter are evaluated in the order
you’ve defined them.
4.3.7.3.2 Messages
• message =string
A message displayed to the user upon log-in.
• hushlogin = ( yes | no )
Setting hushlogin to yes keeps the daemon from displaying motd and user messages upon login.
TACACS+ 33 / 79
Enable passwords may be specified at, in order of preference, host, user or group level:
The following MAVIS related option is available for groups and locally defined users:
member = admins@router
member = staff
...
}
member directives are to be ordered by ACL name, with definitions without ACL coming last. This is enforced at parsing
time.
Alternate groups may be specified, with group names separated by /:
member = dialin/[email protected]
Here, group membership will default to the dialin group. However, appending the separation character * (changeable
using the separation tag = character directive), followed by callback to the username at login time will choose the
corresponding callback group profile instead.
To iterate this again: Any user can be a member of exactly one primary group. The dialin/callback syntax in this
example actually means: The user is free to choose at login time whether he wants to be a member of dialin XOR call
back. User fred may authenticate as fred (or as fred*dialin) to gain dialin membership, and fred*callback
will assign him to the callback group.
The users’ choice can be overridden using the tag directive (see below).
Group membership may depend on NAS IP, e.g. fred could have something like
member = test1@nas1
member = test2@nas2
in his profile.
Just like users, groups can be member of (other) groups:
group = test1 { ... }
group = test2 { ... }
group = test2 {
...
member = test1
member = test2@nas1
...
}
is identical to
group = group1 { }
group = group2 { }
group = group3 { }
user = user1 { member = group1@host1,group2,group3@host3 }
Note: nas default restriction will be ignored for alternate group specifications (e.g. member =group1/gro
up2).
TACACS+ 35 / 79
acl helpdesk = {
time = workinghours
nac = internal_lan
nas = somecustomer
}
acl remoteadmin = {
time = changewindow
nac = dialin
}
user = fred {
login = clear test
member = admin/ro/emergency
tag acl helpdesk = admin
tag acl remoteadmin = emergency
tag = ro
}
fred will be a member of the admin group during work hours, but only if his client IP address is part of the internal LAN
and if he’s on a certain NAS. He’ll be member of the emergency group during change window hours, but needs to come
from the dial-in pool. Outside these hours he’s a member of the ro hours.
tag definitions are evaluated in order. If no definition matches, the user’s session tag (the group selected at login time using
user*group syntax) is used. If no group tag was given, the first group in the member definition is selected.
In most cases, it might be a better idea to use member acl syntax instead of tag acl.
GroupAttr
ShellDecl
ServiceDecl
GroupOnlyAttr
ShellDecl
ServiceDecl
clear cleartextPassword
7 obscuredPassword
permit
deny
PasswdExpr
login
crypt hashedPassword
arap = PasswordExpr
chap
ms-chap
opap
password = PasswdExpr
password Acl { login = PasswordExprHash }
pap
arap = PasswordExpr
chap
ms-chap
opap
EnableExpr
valid from = yyyy-mm-dd
until secondsSinceTheEpoch
message = string
default service = permit
deny
hushlogin = permit
deny
prohibit service = serviceName
client = permit CIDR
deny
hostName
template = groupName
Service definitions may appear in user and group sections. Services can easily be made dependent on ACLs:
service ( acl [ not ] ACLName = service { ... }
There are a couple of generic configuration attributes which may appear in arbitrary service definitions. In particular:
For a detailed description on mandatory and optional AV-pairs, see the "The Authorization Algorithm" section somewhere
below.
• return
Use the current service definition as-is. This stops the daemon from checking for the same service in the groups the current
user (or group) is a member of.
Other configuration attributes are service specific and only valid in certain contexts:
SHELL (EXEC) Service
Shell startup should have an appropriate service
service = shell { }
This directive may appear in cmd sections, too, where it overrides the service section definitions.
• cmd =command { ... }
cmd sections permit or deny commands and command arguments. Example:
TACACS+ 39 / 79
cmd = show {
deny /^(running|startup)-config/
deny tech-support
permit //
message deny = "Try this again and your account will be revoked."
message permit = "This data is to be considered confidentional."
}
cmd = reload {
permit //
message permit = "You hopefully know what you’re doing ..."
}
If you’re unsure what commands and arguments the router actually sends for verification, you may simply modify a user (or
group) profile to display those within a login session. E.g.,
user = ... {
...
debug = CMD
...
service = shell {
...
message debug = "author: ’cmd = %c { permit /^%a$/ }"
...
}
}
will display the configuration snippets to permit the command in PCRE syntax:
Router#conf t
author: ’cmd = configure { permit /^terminal <cr>$/ }’
Non-Shell Services
E.g. for PPP, protocol definitions may be used:
service = ppp {
protocol = ip { set addr = 1.2.3.4 }
}
Use
default protocol = permit
or
default protocol = deny
to specify the default for protocols not explicitly defined within a service declaration. (default: deny).
For a Juniper Networks-specific authorization service, use:
service = junos-exec {
set local-user-name = NOC
# see the Junos documentation for more attributes
}
service = dominionsx {
set port-list = "1 3 4 15"
set user-type = administator # or operator, or observer
}
Quotes
If your router expects double-quoted values (e.g. Cisco Nexus devices do), you can advise the parser to automatically add
these:
service = shell {
shell:roles="\"network-admin\""
}
and
service = shell {
double-quote-values = yes
shell:roles="network-admin"
}
Services can be configured NAS specific by appending @host to the service name. Here, host is the name of a host object, not
an IP address or CIDR range. For example, a user’s PPP address could be static on one particular NAS, but dynamic everywhere
else:
host = dialin { address = ... }
user = ... {
service = ppp { protocol = ip { } }
service = ppp@dialin { protocol = ip { set addr = 1.2.3.4 } }
}
While the same can be achieved using host specific group membership, this adds some flexibility for dynamic authentication
backends.
When used with single-connection and being told so, the daemon tries to remember command context. which permits the
shutdown family of commands for exactly one interface, but denies it for all the others:
user = john {
password = clear doe
service = shell {
default cmd = permit
set priv-lvl = 15
script = {
if (cmd == "") permit # shell startup
permit
}
if (context == FE) {
if (cmd =~ /^shutdown/) permit
if (cmd =~ /^no shutdown/) permit
deny
}
}
cmd = shutdown { deny . }
cmd = no { deny /^shutdown/ }
}
}
4.3.7.8 Examples
Here we declare two users fred and lily, and two groups, admin and staff.
fred is a member of group admin and group admin is in turn a member of group staff. lily is not a member of any
group.
group = admin {
# group admin is a member of group staff
member = staff
service = shell {
set priv-lvl = 15
}
}
group = staff {
# group staff is not a member of any group
}
user = lily {
# user lily is not a member of any group
# and has nothing else configured as yet
}
user = fred {
# fred is a member of group admin on 0.0.0.0/0
member = admin
# fred is a member of group staff when logging in on 10.0.0.0/8
member = [email protected]/8
# fred is a member of group admin when logging in on hosts
# defined in hostgroup test123
member = admin@test123
# fred may only log in from a client in 172.16.0.0/24 ...
client = 172.16.0.0/24
# ... or from whatever address is defined in some host object test123
client = test123
}
ServiceAttr
AttrDefault
ProtoDefault
ProtoDecl
Acl
AVPair
UserMessage
return
ShellAttr
AttrDefault
CmdDefault
Acl
AVPair
TacScript
return
script = { TacAction }
UserMsg
protocol = protocolName
{ AttrDefault }
Acl
AVPair
UserMessage
MAVIS configuration is optional. You don’t need it if you’re content with user configuration in the main configuration file.
MAVIS backends may dynamically create user entries, based, e.g., on LDAP information.
For PAP and LOGIN,
pap backend = mavis
login backend = mavis
in the global section delegate authentiation to the MAVIS sub-system. Statically defined users are still valid, and have a higher
precedence.
By default, MAVIS user data will be cached for 120 seconds. You may change that period using
cache timeout = seconds
Under certain circumstances you may wish to keep the user definitions in the plain text configuration file, but authenticate against
some external system nevertheless, e.g. LDAP or RADIUS. To do so, just specify one of
login = mavis
pap = mavis
password = mavis
In general, when the daemon looks up values, it will look first to see if the user has her own definition for that value. If not, it
looks to see if she belongs to a group and if so, whether the group has a value defined. If not, this process continues through the
hierarchy of groups (a group can be a member of another group) until a value is found, or there are no more gmaryroups.
This recursive process occurs for lookups of expiration dates and also for authorization parameters (see later), but not for user
passwords.
A typical configuration technique is thus to place users into groups and specify as many groupwide characteristics in the group
declaration as possible. Then, individual user declarations can be used to override the group settings for selected users as needed.
User Authentication can be specified separately for PAP, ARAP, CHAP, and normal logins. ARAP, CHAP, and global user
authentication must be given in clear text.
The following assigns the user mary five different passwords for ARAP, inbound and outbound CHAP, inbound PAP, outbound
PAP, and normal login respectively:
user = mary {
arap = clear "arap password"
chap = clear "chap password"
pap = clear "inbound pap password"
opap = clear "outbound pap password"
login = crypt XQj4892fjk
}
If
user backend = mavis
is configured in the global section, users not found in the configuration file will be looked up by the MAVIS backend. You
should consider using this option in conjuction with the more sophisticated backends (LDAP and ActiveDirectory, in particular),
or whenever you’re not willing to duplicate your pre-existing database user data to the configuration file. For users looked up by
the MAVIS backend,
pap backend = mavis
and/or
login backend = mavis
(again, in the global section of the configuration file) will cause PAP and/or Login authentication to be performed by the MAVIS
backend (e.g. by performing an LDAP bind), ignoring any corresponding password definitions in the users’ profile.
If you just want the users defined in your configuration file to authenticate using the MAVIS backend, simply set the correspond-
ing PAP or Login password field to mavis (there’s no need to add the user backend =mavis directive in this case):
user = mary { login = mavis }
will cause the profile to become invalid, starting after the valid until date. Valid date formats are both ISO8601 and the
absolute number of seconds since 1970-01-01.
A expiry warning message is sent to the user when she logs in, by default starting at 14 days before the expiration date, but
configurable via the warning period directive.
Complementary to profile expiry,
valid from = YYYY-MM-DD
(Alternatively, you can try a named authentication list instead of default. Please see the IOS documentation for details.)
If all else fails, and you find yourself locked out of the NAS due to a configuration problem, the section on recovering
from lost passwords on Cisco’s CCO web page will help you dig your way out.
Authorization must be configured on both the NAS and the daemon to operate correctly. By default, the NAS will allow every-
thing until you configure it to make authorization requests to the daemon.
On the daemon, the opposite is true: The daemon will, by default, deny authorization of anything that isn’t explicitly permitted.
Authorization allows the daemon to deny commands and services outright, or to modify commands and services on a per-user
basis. Authorization on the daemon is divided into two separate parts: commands and services.
Exec commands are those commands which are typed at a Cisco exec prompt. When authorization is requested by the NAS, the
entire command is sent to the tac_plus daemon for authorization.
Command authorization is configured by specifying a list of PCRE (Perl Compatible Regular Expressions) or egrep-style
(POSIX 1003.2) regular expressions to match command arguments (see your local pcrematching(3) or regex(7) man page,
respectively, for a description of regular expressions), and an action which is deny or permit.
TACACS+ 46 / 79
Case-insensitive pattern-matching
By default, regular expression matching is case-insensitive. This can be changed by specifying
regex-match-case = yes
If a POSIX pattern contains spaces or special characters, you’ll have to enclose the pattern in double quotes ("). For text inside
double quotes, C parsing rules apply. In particular, the backslash \ needs to be escaped as \\. PCRE patterns need to be enclosed
in slashes (/pattern/). Both POSIX and PCRE syntax may be used in the same configuration file.
The following configuration example permits user Fred to run the following commands:
telnet 131.108.13.any_number
telnet 128.any_number.12.3
show running-config interface anything
Whitespace
If any non-PCRE argument list you specify contains spaces or tabs, you must enclose it in double quotes. PCRE expressions
are to be enclosed in slashes, anyway.
The command and arguments which the user types gets matched to the regular expressions you specify in the configuration file
(in order of appearance). The first successful match performs the associated action (permit or deny). If there is no match, the
command is denied by default.
Conversely, the following configuration example can be used to deny the command:
telnet 131.108.13.any_number
and permit all other arguments, since the last line will match any argument list. All other commands and services are permitted
due to the "default service = permit" clause.
Note: the default statement must be the first in the user clause
user=fred {
default service = permit
service = shell {
cmd = telnet {
# allow all telnet commands except to 131.108.13.*
TACACS+ 47 / 79
deny ^131\.108\.13\.[0-9]+
permit .*
}
}
}
Matches are only anchored if you specify so, so deny 131.108.13.[0-9]+ matches anywhere in the command. To anchor
the match, use ˆ at the beginning of the regular expression.
When a command has multiple arguments, users may enter them in many different permutations. It can be cumbersome to create
regular expressions which will reliably authorize commands under these conditions, so administrators may wish to consider other
methods of performing authorization e.g. by configuring NAS-local privileged enable levels on the NAS itself.
For command authorization, the Cisco NAS expands all commands to their full names e.g. when you type config t on the
NAS, this will be expanded to configuration terminal before being sent to the daemon so that you don’t need to list all
the possible contractions of a command.
At the user level i.e. inside the braces of a user declaration, the default for a user who doesn’t have a service or command
explicitly authorized is to deny that service or command. The following directive will permit the service or command by default
instead:
user = lol {
default service = permit
}
Note: This directive must appear first inside the user declaration.
At the service authorization level i.e. inside the braces of a service declaration, arguments in an authorization request are
processed according to the algorithm described later. Some actions when authorizing services (e.g. when matching attributes are
not found) depend on how the default is configured. The following declaration changes the default from deny to permit for this
user and service.
user = lol { service = shell { default attribute = permit } }
Note: This directive must appear before any others inside the service declaration.
Note: for command authorization (as opposed to service authorization being discussed here), you specify deny .* or permit .* as
the last line of the regular expression matches to create default behavior.
You can supply some parameters whenever an shell starts, e.g. an autocommand, a dialback string or a connection access list
(acl). In the example below, when an exec is started on the NAS, an acl of 4 will be returned to the NAS:
user=fred {
service = shell {
# When an exec is started, its connection access list will be 4.
# It also has an autocmd.
set acl = 4
set autocmd = "telnet foobar"
cmd = telnet {
TACACS+ 48 / 79
Note: specifying an autocommand, or any other exec services, is part of EXEC AUTHORIZATION. For it to work, you must
also configure exec authorization on your NAS e.g.
aaa authorization exec authorlist group tacacs+ local
aaa authorization commands 15 cmdlist group tacacs+ local
Just as with authentication, you can use a named authorization list, or simply default.
Authorizing EXEC, SLIP, PPP and ARAP services is done quite differently from command authorization.
When authorizing these services, the NAS sends a request containing a number of attribute-value (AV) pairs, each having the
form
attribute=value
(Note: during debugging, you may see AV pairs whose separator character is a * instead of a = sign, meaning that the value in a
pair is optional. An = sign indicates a mandatory value. A * denotes an optional value).
E.g., a user starting PPP/IP using an address of 131.108.12.44 would generate a request with the following AV pairs:
service=ppp
protocol=ip
addr*131.108.12.44
to see what authorization AV pairs are being used by the NAS. Note: If you are not on the router console, you will also need to
issue a terminal monitor command to see debug output.
Authorizing a single session can result in multiple requests being sent to the daemon. For example, in order to authorize a dialin
PPP user for IP, the following authorization requests will be made from the NAS:
1. An initial authorization request to startup PPP from the exec, using the AV pairs service=ppp, protocol=ip, will
be made (Note: this initial request will be omitted if you are autoselecting PPP, since you won’t know the username yet).
This request is really done to find the address for dumb PPP (or SLIP) clients who can’t do address negotiation. Instead,
they expect you to tell them what address to use before PPP starts up, via a text message e.g. "Entering PPP. Your address
is 1.2.3.4". They rely on parsing this address from the message to know their address.
2. Next, an authorization request is made from the PPP subsystem to see if PPP’s LCP layer is authorized. LCP parameters
can be set at this time (e.g. callback). This request contains the AV pairs service=ppp, protocol=lcp.
TACACS+ 49 / 79
3. Next an authorization request to startup PPP’s IPCP layer is made using the AV pairs service=ppp, protocol=ipcp.
Any parameters returned by the daemon are cached.
4. Next, during PPP’s address negotiation phase, each time the remote peer requests a specific address, if that address isn’t in
the cache obtained in step 3, a new authorization request is made to see if the peers requested address is allowable. This
step can be repeated multiple times until both sides agree on the remote peer’s address or until the NAS (or client) decide
they’re never going to agree and they shut down PPP instead.
Since we pretty much rely on having a username in authorization requests to decide which addresses etc. to hand out, it is
important to know where the username for a PPP user comes from. There are generally 2 possible sources:
1. You force the user to authenticate by making her login to the exec and you use that login name in authorization requests.
This username isn’t propagated to PPP by default. To have this happen, you generally need to configure the if-needed
method, e.g.
aaa authentication login default tacacs+
aaa authentication ppp default if-needed
2. Alternatively, you can run an authentication protocol, PAP or CHAP (CHAP is much preferred), to identify the user. You
don’t need an explicit login step if you do this (so it’s the only possibility if you are using autoselect). This authentication
gets done before you see the first LCP authorization request of course. Typically you configure this by doing:
aaa authentication ppp default tacacs+
int async 1
ppp authentication chap
If you omit either of these authentication schemes, you will start to see authorization requests in which the username is missing.
A list of AV pairs is placed in the daemon’s configuration file in order to authorize services. The daemon compares each NAS
AV pair to its configured AV pairs and either allows or denies the service. If the service is allowed, the daemon may add, change
or delete AV pairs before returning them to the NAS, thereby restricting what the user is permitted to do.
The complete algorithm by which the daemon processes its configured AV pairs against the list the NAS sends, is given below.
Find the user (or group) entry for this service (and protocol), then for each AV pair sent from the NAS:
(c) If no mandatory match exists, look for an exact attribute,value pair match among the daemon’s optional AV pairs. If
found add the DAEMON’s matching AV pair to the output.
(d) If no exact match exists, locate the first attribute match among the daemon’s optional AV pairs. If found add the
DAEMON’s matching AV pair to the output.
(e) If no match is found, delete the AV pair if the default is deny, or
(f) If the default is permit add the NAS AV pair to the output.
3. After all AV pairs have been processed, for each mandatory DAEMON AV pair, if there is no attribute match already in
the output list, add the AV pair (but add only ONE AV pair for each mandatory attribute).
Remember that authorization is also recursive over groups. Thus, if you place a user in a group, the daemon will look in the
group for authorization parameters if it cannot find them in the user declaration.
4.3.7.23 Examples
host = 0.0.0.0/0 {
key = "your key here"
}
group = admin {
# group members who have no expiry date set will use this one
valid until = 1997-01-01
service = shell {
default cmd = permit
}
}
user=fred {
login = crypt mEX027bHtzTlQ
member = admin
valid until = 2005-05-23
service = shell {
# When Fred starts an exec, his connection access list is 5
set acl = 5
service = ppp {
protocol = lcp
protocol = ip {
# Fred can run IP over PPP only if he uses one
# of the following mandatory addresses. If he
# supplies no address, the first one here will
# be mandated
set addr=131.108.12.11
set addr=131.108.12.12
set addr=131.108.12.13
TACACS+ 51 / 79
set addr=131.108.12.14
service = slip {
default protocol = permit
# Fred can run SLIP. When he does, he will have to use
# these mandatory access lists
set inacl=101
set outacl=102
}
}
user = wilma {
login = crypt mEX027bHtzTlQ
member = admin
}
If authorization is not explicitly configured on the NAS, no authorization takes place, i.e. effectively, everything is permitted.
Note that this is the converse of what happens on the daemon, where anything not explicitly permitted is denied by default.
To configure command authorization on the NAS, issue the following NAS configuration commands:
aaa authorization commands 15 cmdlist group tacacs+ local
This will make the NAS send TACACS+ requests for all level 1 (ordinary user) and level 15 (privileged level) commands.
Note: As soon as you configure the above on your NAS, you will only be permitted to execute NAS commands which are
permitted by your TACACS+ daemon. So make sure you have configured, on the daemon, an authenticated user who is authorized
to run commands, or you will be unable to do much on the NAS after turning on authorization.
Alternatively, or in addition, you may also want to configure the following:
aaa authorization commands 1 group tacacs+ if-authenticated
This will use TACACS+ authorization for level 1 (user-level commands) but if problems arise, you can just switch off the
TACACS+ server and authorization will then be granted to anyone who is authenticated.
The following daemon configuration should be sufficient to ensure that you can always login as username admin (with a suitable
password) and run any command as that user:
user = admin {
default service = permit
service = shell { default cmd = permit }
login = crypt kppPfHq/j6gXs
}
TACACS+ 52 / 79
5 MAVIS Backends
The distribution comes with various MAVIS modules, of which the external module is probably the most interesting, as it interacts
with simple Perl scripts to authenticate and authorize requests. You’ll find sample scripts in the mavis/perl directory. Have a
close look at them, as you may (or will) need to perform some trivial customizations to make them match your local environment.
You should really have a look at the MAVIS documentation. It gives examples for RADIUS and PAM authentication, too.
mavis_tacplus_ldap.pl is an authentication/authorization backend for the external module. It interfaces to various kinds
of LDAP servers, e.g. OpenLDAP, Fedora DS and Active Directory. Its behaviour is controlled by a list of environmental
variables:
For LDAP_SERVER_TYPE set to tacacs_schema, the program expects the LDAP server to support the experimental ldap.
schema, included for OpenLDAP and Fedora-DS. The schema files are located in the mavis/perl directory.
The new schema allows for a auxiliary object class
objectClass: tacacsAccount
which introduces a couple of new attributes. A sample user entry could then look similar to the following LDIF snippet:
dn: uid=marc,ou=people,dc=example,dc=com
uid: marc
cn: Marc Huber
objectClass: posixAccount
objectClass: inetOrgPerson
objectClass: shadowAccount
objectClass: tacacsAccount
shadowMax: 10000
uidNumber: 1000
gecos: Marc Huber
givenName: Marc
sn: Huber
gidNumber: 500
shadowLastChange: 14012
loginShell: /bin/bash
homeDirectory: /Users/marc
mail: [email protected]
userPassword:: abcdefghijklmnopqrstuvwxyz=
tacacsClient: 192.168.0.0/24
tacacsClient: management
tacacsMember: [email protected]/24
tacacsMember: readwrite@nasgroup
tacacsProfile: { valid until = 2010-01-30 chap = clear ahzoi5Ue }
As tacacsProfile may (and most probably will) contain sensitive data, you should consider setting up LDAP ACLs to
restrict access.
You should be pretty familiar with OpenLDAP (or, for that matter, Fedora-DS) if you’re willing to go this route. For current
versions of OpenLDAP: Use ldapadd to add tacacs_schema.ldif to the cn=config tree. For older versions, add
tacacs.schema to the list of included schema and objectClass definitions in slapd.conf.
TACACS+ 53 / 79
Variable Description
One of: generic, tacacs_schema, microsoft.
LDAP_SERVER_TYPE
Default: tacacs_schema
Space-separated list of LDAP URLs or IP addresses or hostnames
LDAP_HOSTS Examples:
"ldap01 ldap02", "ldaps://ads01:636 ldaps://ads02:636"
LDAP search scope (base, one, sub)
LDAP_SCOPE
Default: sub
Base DN of your LDAP server
LDAP_BASE
Example: dc=example,dc=com
LDAP search filter. Defaults:
• for LDAP_SERVER_TYPE=generic:
"(uid=%s)"
User to use for LDAP bind if server doesn’t permit anonymous searches.
LDAP_USER
Default: unset
Password for LDAP_USER
LDAP_PASSWD
Default: unset
An AD group starting with this prefix will be used as the user’s TACACS+ group
membership. The value of AD_GROUP_PREFIX will be stripped from the group
name.
AD_GROUP_PREFIX
Example: With AD_GROUP_PREFIX set to tacacs (which is actually the
default), an AD group membership of TacacsNOC will assign the user to the NOC
TACACS+ group. Note that TACACS+ group names are case-sensitive.
If set, user needs to be in one of the AD_GROUP_PREFIX groups.
REQUIRE_AD_GROUP_PREFIX
Default: unset
If set, the server is required to support start_tls.
USE_TLS
Default: unset
Permit password changes via this backend.
FLAG_CHPW
Default: unset
Try to enforce a simplicistic password policy.
FLAG_PWPOLICY
Default: unset
Keep connection to LDAP server open.
FLAG_CACHE_CONNECTION
Default: unset
If searching for the user in LDAP fails, try the next MAVIS module (if any).
FLAG_FALLTHROUGH
Default: unset
Use the memberOf attribute for determining group membership. Setting
LDAP_SERVER_TYPE to microsoft implies this. May be used if you’re
FLAG_USE_MEMBEROF
running OpenLDAP with memberof overlay enabled.
Default: unset
id = tac_plus {
access log = /var/log/tacacs/%Y/%m/%d/access.log
accounting log = /var/log/tacacs/%Y/%m/%d/acct.log
host = world {
address = ::/0
welcome banner = "Welcome\n"
key = cisco
}
host = helpdesklab {
address = 192.168.34.16/28
}
group = admin {
default service = permit
service = shell {
TACACS+ 55 / 79
group = helpdesk {
default service = permit
service = shell {
default command = permit
default attribute = permit
set priv-lvl = 1
}
enable = deny
member = admin@helpdesklab
}
}
If LDAP_SERVER_TYPE is set to generic, the script won’t require any modification to your LDAP server, but only authen-
ticates users (with login =mavis, pap =mavis or password =mavis declaration) defined in the configuration file. No
authorization is done by this backend.
id = tac_plus {
mavis module = groups {
resolve gids = yes
groups filter = /^(guest|staff)$/
script out = {
# copy the already filtered UNIX group access list to TACMEMBER
eval $GIDS =~ /^(.*)$/
set $TACMEMBER = $1
}
}
mavis module = external {
exec = /usr/local/sbin/pammavis pammavis -s sshd
}
user backend = mavis
login backend = mavis
host = global { address = 0.0.0.0/0 key = cisco }
group = staff {
service = shell {
default command = permit
set priv-lvl = 15
}
}
group = guest {
service = shell {
default command = deny
TACACS+ 56 / 79
set priv-lvl = 15
cmd = show { permit .* }
}
}
}
mavis_tacplus_passwd.pl authenticates against your local password database. Alas, to use this functionality, the script
may have to run as root, as it needs access to the encrypted passwords. Primary and auxiliary UNIX group memberships will be
mapped to TACACS+ groups.
mavis_tacplus_opie.pl is based on mavis_tacplus_passwd.pl, but uses OPIE one-time passwords for authenti-
cation.
id = spawnd {
listen = { port = 49 }
}
id = tac_plus {
mavis module = external {
exec = /usr/local/lib/mavis/mavis_tacplus_opie.pl
}
login backend = mavis chalresp
host = global { address = 0.0.0.0/0 key = cisco }
group = staff {
service = shell {
default command = permit
set priv-lvl = 15
}
}
user = user1 { password = mavis member = staff }
user = user2 { password = clear pass2 member = staff }
}
mavis_tacplus_shadow.pl may be used to keep user passwords out of the tac_plusconfiguration file, enabling users to
change their passwords via the password change dialog. Passwords are stored in an auxiliary, /etc/shadow-like ASCII file,
one user per line:
username:encryptedPassword:lastChange:minAge:maxAge:reserved
lastChange is the number of days since 1970-01-01 when the password was last changed, and minAge and maxAge deter-
mine whether the password may/may not/needs to be changed. Setting lastChange to 0 enforces a password change upon
first login.
Example shadow file:
marc:$1$q5/vUEsR$jVwHmEw8zAmgkjMShLBg/.:15218:0:99999:
newuser:$1$pQtQsMuj$GKpIr5r2GNaZNfDfnCBtw.:0:0:99999:
test:$1$pQtQsMuj$GKpIr5r2GNaZNfDfnCBtw.:15218:1:30:
mavis_tacplus_radius.pl authenticates against a RADIUS server. No authorization is done, unless the RADIUS_GROU
P_ATTR environment variable is set (see below). This may, for example, be useful if you have static user account definitions in
the configuration file, but authentication passwords should be verified by RADIUS. Use the login =mavis or password =
mavis statement in the user profile for this to work.
If the Authen::Radius Perl module is installed, the value of the RADIUS attribute specified by RADIUS_GROUP_ATTR
will be used to create a TAC_PROFILE definition which uses the attribute value as group membership. E.g., an attribute value
of Administrator would result in a
{ member = Administrator }
profile for the authenticated user, enabling authorization and omitting the need for static users in the configuration file.
Keep in mind that this will only work if
• mavis cache timeout is set to a sufficiently high value that covers the user’s (expected) maximum login time.
mavis_tacplus_sms.pl is a sample (skeleton) script to send One-Time Passwords via a SMS backend.
If a backend script fails due to an external problem (e.g. LDAP server unavailability), your router may or may not fall back to
local authentication (if configured). Chances are, that the fallback doesn’t work. If you still want to be able to authenticate via
TACACS+ in that case, you can do so with a non-MAVIS user which will only be valid in case of a backend error:
TACACS+ 58 / 79
...
# set the time interval you want the user to be valid if the backend fails:
authentication fallback period = 60 # that’s actually the default value
...
# add a local user for emergencies:
user = cisco {
...
fallback-only
...
}
To indicate that fallback mode is actually active, you may a display a different login prompt to your users:
host = ... {
...
welcome banner = "Welcome\n"
welcome banner fallback = "Welcome\nEmergency accounts are currently enabled.\n"
...
}
6 Debugging
When creating configuration files, it is convenient to check their syntax using the -P flag to tac_plus; e.g:
tac_plus -P config-file
will syntax check the configuration file and print any error messages on the terminal.
Trace (or debugging) options may be specified in global, host, user and group context. The current debugging level is a combi-
nation (read: OR) of all those. Generic syntax is:
debug = option ...
For example, getting command authorization to work in a predictable way can be tricky - the exact attributes the NAS sends to
the daemon may depend on the IOS version, and may in general not match your expectations. If your regular expressions don’t
work, add
debug = REGEX
where appropriate, and the daemon may log some useful information to syslog.
Multiple trace options may be specified. Example:
debug = REGEX CMD
TACACS+ 59 / 79
The debugging options available are summarized in the Debug and Trace Values table.
Some of those debugging options are not used and trigger no output at all.
number
ACCT
ACL
AUTHEN
AUTHOR
CMD
CONFIG
HEX
LOG
NONE
PACKET
PARSE
REGEX
LWRES
The TACACS+ protocol includes specific protocol knowledge for both ARAP and CHAP. (This version of the daemon imple-
mentation no longer supports the SENDPASS mechanism.)
Note that the above doesn’t apply to PAP. You can keep an inbound PAP password DES- or MD5-encrypted, since all you need
to do with it is verify that the password the principal gave you is correct.
You use this to set the outbound PAP password. It must be a cleartext password.
Passwords
It is very bad practice to use an outbound PAP password that is the same as any of your inbound passwords. For this
reason, a "global" password, defined with the password =clear ... statement, does not apply to outbound PAP, only
to inbound PAP, bidirectional CHAP and ARAP.
• How can I deny telneting from a commserver by IP address only i.e. when command is 10.0.1.6 rather than telnet
10.0.1.6?
The best way to restrict telnet access is by applying an outbound access list via the access class command (or equivalently, via
the "acl" avpair). The NAS configuration command
access-class n out
for example applies a pre-defined standard IP access list (where n is a number from 1 through 99) that governs telnet access
from a NAS.
E.g. the following configuration commands permit outgoing Telnet access from line 1 on the NAS *only* to hosts on network
192.85.55.0:
TACACS+ 62 / 79
Note: You must define the access-list on the NAS. Only then can you use the acl avpair to apply it to a line that a user dials in
on.
Alternatively, you can try configuring transport preferred none on the lines in question. This will force a user to
always type telnet 10.0.1.6 in order to telnet out from the NAS. Then you can apply command authorization to this
command to restrict it.
• I have an autocommand configured in the NAS-local database and I’m using
aaa authentication local-override
This will use the local DB entry if one exists, allow authenticated users otherwise, or fail.
We don’t have a aaa authorization local-override like we do for authentication. Unlike authentication, the local
method for authorization is sort of equivalent to a local-override.
• Can TACACS+ only be enabled on a global basis?
You turn TACACS+ ON on a global basis, but you can then change the behavior of individual lines to whatever you want, e.g.
aaa authentication login default tacacs+ none
aaa authentication login oldstyle line
aaa authentication login none none
line 1 16
login authentication default
line vty 0 4
login authentication oldstyle
line 0
login authentication none
Note that unfortunately, you can’t (yet) apply authorization differently to selected lines and interfaces.
• I have leased lines running PPP, and AAA authorization is also configured, so the authorization on the leased lines fails.
What should I do?
Since you can’t (yet) configure authorization on a per-line basis, you have to turn on authentication on the leased lines running
PPP and configure your TACACS+ server so that it will authorize these lines correctly.
A more demanding alternative is to modify the TACACS+ server source code to allow any authorizations coming in from the
port "SerialXXX" to succeed.
A third possibility is to not use PPP on those lines, e.g. use HDLC instead. HDLC doesn’t require authentication or authoriza-
tion.
• How many characters may a TACACS+ Username and Password contain?
The short answer is 31 bytes of username, with up to 254 bytes of password if they are cleartext (8 bytes if passwords are DES
encrypted).
The long answer is that the Cisco NAS allocates a buffer of 1024 bytes, so this is the maximum you can type in, in response to
a NAS prompt.
But the protocol spec allows a username or password length field of just one byte in an authentication packet, so only the first
255 of these characters can be sent to the daemon.
Now if it’s a DES encrypted password, then only the first 8 bytes are significant, per the common unix implementations of
crypt.
TACACS+ 63 / 79
You also need to configure exec authorization on the NAS for the above timeouts, e.g.
aaa authorization exec default group tacacs+
Note that these timeouts only work for async lines, not for ISDN currently.
Note also that you cannot use the authorization if-authenticated option with these parameters, since that skips autho-
rization if the user has successfully authenticated.
• How do I send VPDN forwarding decisions to an authorization server?
In 11.2 onwards, VPDN NASs can use TACACS+ to allow an authorization server to make the decision to forward users.
If VPDN forwarding is turned on, and the username is of the form user@domain, and domain matches a vpdn outgoing
configured domain, then an authorization attempt is made for domain (see below).
When making an authorization call for VPDN, a service type of ppp with a protocol type of vpdn, with a username of
domain will be made e.g. when a PPP user comes up on a line with the username [email protected], if vpdn enable and
aaa authorization .... is enabled on the box, then a one-time authorization of the name bar.com is attempted.
If this authorization is successful, no local authentication is attempted on the NAS, and the connection is forwarded via VPDN
instead.
If no VPDN-specific information comes back from this authorization call, the login proceeds as follows:
If tacacs-server directed-requests are configured (note: this is true by default), then IOS will strip off the domain
part of a name of the form user@domain and use "domain" to try and select a TACACS+ server. If successful, the username
portion "user", without the domain, will be used for all subsequent authentication, authorization and accounting.
If directed requests are turned off, then the entire username user@domain is treated as a username.
vpdn specific information includes the attributes tunnel-id, source-ip (deprecated) and ip-addresses:
TACACS+ syntax
The following syntax is used:
user = domain {
service = ppp {
protocol = vpdn {
set tunnel-id = name for tunnel authentication
set ip-addresses = addr [addr ...]
set source-ip = ip-address
}
}
}
In addition the TACACS+ server can be used to store the usernames for both the NAS (the username specified by "tunnel-id"
above) and the Home Gateway. These will be used to authenticate the tunnel.
Example:
user = foobar.example.com {
service = ppp {
protocol = vpdn {
set tunnel-id = my_nas
set ip-addresses = "203.0.113.19 203.0.113.20"
set source-ip = 197.51.100.1
}
}
}
IOS Configuration
To enable AAA assisted VPDN forwarding on a NAS, the following minimum configuration is required:
vpdn enable
aaa new-model
aaa authorization network default group tacacs+ ...
In addition, if the passwords for the home gateway and NAS are stored on the TACACS+ daemon, the command
aaa authentication login default group tacacs+ ....
Daemon:
user = lol { service = ppp { protocol = ip { set addr-pool = foo } } }
8 Canned Configurations
A canned configuration for login authentication only. This allows user fred to login with password abcdef. If the TACACS+
server dies, the enable secret will be accepted as a login password instead.
Daemon:
id = spawnd {
listen = { port = 49 }
}
id = tac_plus {
host = any { key = "some key" address = 0.0.0.0/0 }
NAS:
aaa new-model
enable secret foobar
! use TACACS+. If server dies, use the enable secret password
aaa authentication login default group tacacs+ enable
tacacs-server host ...
tacacs-server key some key
TACACS+ 66 / 79
This will allow user fred to login with password abcdef and to run the privileged (level 15) commands write terminal
and configure. All other privileged commands will be denied.
The "none" keyword in the NAS configuration line means that if the TACACS+ server dies, any command will be allowed.
Daemon:
id = spawnd {
listen = { port = 49 }
}
id = tac_plus {
host = any { key = "some key" address = 0.0.0.0/0 }
NAS:
aaa new-model
! all level 15 (privileged commands). If server dies, allow everything
aaa authorization commands 15 default group tacacs+ none
tacacs-server host 10.1.1.1
tacacs-server key some key
This config allows fred to login to line 1 with password abcdef (or to and to run ppp using chap authentication. The chap
password is lab.
Daemon:
id = spawnd {
listen = { port = 49 }
}
id = tac_plus {
host = any { key = "some key" address = 0.0.0.0/0 }
NAS:
aaa new-model
! authenticate exec logins (if not autoselecting)
aaa authentication login default group tacacs+
! authorize network services via TACACS+
aaa authorization network default group tacacs+
TACACS+ 67 / 79
8.4 ARAP
Daemon:
id = spawnd {
listen = { port = 49 }
}
id = tac_plus {
host = any { key = "some key" address = 0.0.0.0/0 }
user = lol {
arap = clear arapSecret
service = arap
}
}
NAS:
aaa new-model
aaa authentication arap default group tacacs+
aaa authorization network default group tacacs+
aaa accounting network default start-stop group tacacs+
!
appletalk routing
arap network ...
!
interface Ethernet0
appletalk cable-range ...
appletalk zone ...
!
tacacs-server host ...
tacacs-server key ...
!
line 1
location a modem
modem answer-timeout 0
modem InOut
autoselect arap
autoselect during-login
arap enable
speed ...
flowcontrol hardware
TACACS+ 68 / 79
8.5 Callback
Availability of Callback
Callback is available only in IOS 11.1 and later, and can only be controlled via TACACS+ for ASYNC lines. ISDN callback can
be configured on the NAS but cannot be controlled via AAA.
Here is an example of AAA configuration (with exec and network accounting enabled):
Daemon:
Example of remote TACACS+ server configuration file entry for username foobar:
id = spawnd {
listen = { port = 49 }
}
id = tac_plus {
host = any { key = "some key" address = 0.0.0.0/0 }
user = foobar {
arap = clear AAAA
login = clear LLLL
chap = clear CCCC
pap = clear PPPP
opap = clear OOOO
service = ppp {
default protocol = permit
protocol = lcp {
set callback-dialstring=123456
}
}
service = arap {
protocol = atalk {
set callback-dialstring=2345678
}
}
service = shell {
set callback-dialstring=3456789
set callback-line=7
set nocallback-verify=1
}
}
}
NAS:
aaa new-model
tacacs-server host XX.XX.XX.XX
tacacs-server key fookey
aaa accounting exec wait-start tacacs+
aaa accounting network wait-start tacacs+
line 4
login authentication execcheck
line 4
arap authentication arapcheck
int async 6
ppp authentication chap pppcheck
ppp callback accept
9 Authorization AV pairs
The following authorization AV pairs specify which service is being authorized and are typically accompanied by protocol AV
pairs and other, additional pairs from the lists below. (For a complete and current list of supported AV pairs and required IOS
releases search http://www.cisco.com/ for "TACACS+ Attribute-Value Pairs".)
• service=arap
• service=shell
Used for exec startup, and also for command authorizations.
• service=ppp
• service=slip
• service=system
Not used
• service=raccess
Used for managing reverse telnet connections, e.g.:
user = jim {
login = clear lab
service = raccess {
set port#1 = nasname1/tty2
set port#2 = nasname2/tty5
}
}
• protocol=atalk
Used with service=ppp or service=arap.
• protocol=vines
For VINES over PPP.
• protocol=ccp
Authorization of CCP (Compression Control Protocol). No other av-pairs associated with this.
• protocol=cdp
Authorization of CDP (Cisco Discovery Protocol). No other av-pairs associated with this.
• protocol=multilink
Authorization of multilink PPP. See max-links and load-threshold.
• protocol=unknown
For undefined/unsupported conditions. Should not occur under normal circumstances.
• cmd (EXEC)
If the value of cmd is NULL e.g. the AV pair is cmd=, then this is an authorization request for starting an exec (shell). If
cmd is non-null, this is a command authorization request, It contains the name of the command being authorized, e.g. cmd=
telnet.
• cmd-arg (EXEC)
During command authorization, the name of the command is given by an accompanying cmd= AV pair, and each command
argument is represented by a cmd-arg AV pair e.g. cmd-arg=archie.sura.net Note: cmd-arg should never appear
in a configuration file. It is used internally by the daemon to construct a string which is then matched against the regular
expressions which appear in a cmd clause in the configuration file.
An outbound access-class is the best way to restrict outgoing telnet connections. Note that a suitable access list (in this case,
numbered 2) must be predefined on the NAS.
• inacl (PPP/IP/IPX)
This AV pair contains an IP or IPX input access list number for slip or PPP e.g. inacl=2. The access list itself must be
pre-configured on the Cisco box. Per-user access lists do not work with ISDN interfaces unless you also configure a virtual
interface. After 11.2(5.1)F, you can also use the name of a predefined named access list, instead of a number, for the value of
this attribute. Note: For IPX, inacl is only valid after 11.2(4)F.
Attributes are sorted numerically before they are applied. For IP, standard OR extended access list syntax may be used, but it
is an error to mix the two within a given access-list. For IPX, only extended access list syntax may be used. See also:
TACACS+ 71 / 79
sho ip access-lists
sho ip interface
sho ipx access-lists
sho ipx interface
debug aaa author
debug aaa per-user
Attributes are sorted numerically before they are applied. For IP, standard OR extended access list syntax may be used, but it
is an error to mix the two within a given access-list. For IPX, only extended access list syntax may be used.
See also:
sho ip access-lists
sho ip interface
sho ipx access-lists
sho ipx interface
debug aaa author
debug aaa per-user
• autocmd (EXEC)
During exec startup, this specifies an autocommand, like the autocommand option to the username configuration command,
e.g. autocmd="telnet foo.com".
• noescape (EXEC)
During exec startup, this specifies "noescape", like the noescape option to the username configuration command. Can have as
its value the string true or false, e.g. noescape=true.
• nohangup (EXEC)
During exec startup, this specifies nohangup, like the nohangup option to the username configuration command. Can have
as its value the string true or false, e.g. nohangup=true.
• priv-lvl (EXEC)
Specifies the current privilege level for command authorizations, a number from zero to 15 e.g. priv-lvl=5. Note: in 10.3
this attribute was priv_lvl i.e. it contained an underscore instead of a hyphen.
TACACS+ 72 / 79
• zonelist (ARAP)
An Appletalk zonelist for arap equivalent to the line configuration command arap zonelist, e.g. zonelist=5.
• addr-pool (11.0 onwards, PPP/IP, SLIP)
This AV pair specifies the name of a local pool from which to get the IP address of the remote host. Note: addr-pool works
in conjunction with local pooling. It specifies the name of a local pool (which needs to be pre-configured on the NAS). Use the
ip-local pool command to declare local pools, e.g on the NAS:
ip address-pool local ip local pool foo 1.0.0.1 1.0.0.10
ip local pool baz 2.0.0.1 2.0.0.20
Then you can use TACACS+ to return addr-pool=foo or addr-pool=baz to indicate which address pool you want to
get this remote node’s address from, e.g. on the daemon:
user = lol { service = ppp { protocol = ip { set addr-pool=foo } }
This indicates a temporary static route that is to be applied. dst_address, mask and gateway are expected to be in the usual
dotted-decimal notation, with meanings the same as for the familiar ip route configuration command on a NAS. If gateway
is omitted, the peer’s address is taken to be the gateway. The route is expunged once the connection terminates.
• route#n (PPP/IP/IPX, 11.2(4)F)
Same as the route attribute, except that these are valid for IPX as well as IP, and they are numbered, allowing multiple routes
to be applied e.g.
route#1="3.0.0.0 255.0.0.0 1.2.3.4"
route#2="4.0.0.0 255.0.0.0"
See also:
sho ip route
sho ipx route
debug aaa author
debug aaa per-user
1. comma (’,’) is also considered as a delimiter. For example the avpair can now be written as
ip-addresses = 172.21.9.26,172.21.9.15,172.21.9.4
2. slash (’/’) is considered a priority delimiter. When you have a number of Home Gateway routers, it is desirable to
consider some as the primary routers and some as backup routers. ’/’ allow you to config the routers into priority groups,
so that the NAS will try to forward the users to the high priority routers, before forwarding to the low priority one.
For example in the following av-pair:
ip-addresses = "172.21.9.26 / 172.21.9.15 / 172.21.9.4"
See also:
TACACS+ 74 / 79
show ip access-lists
show ip protocols
sho ipx access-lists
sho ipx interface
debug aaa author
debug aaa per-user
router ...
[no] distribute-list ...
See also:
sho ipx access-lists
sho ipx interface
show ip access-lists
show ip protocols
debug aaa author
debug aaa per-user
router ...
[no] distribute-list ...
The syntax of static saps is the same as that used by the IOS ipx sap command.
See also:
sho ipx servers
debug aaa author
debug aaa per-user
[no] ipx sap ...
See also:
sho ip route
sho ipx route
debug aaa author
debug aaa per-user
where nas-name is the configured name of the NAS. Note: This username can be changed using the IOS configuration directive
e.g.
TACACS+ 76 / 79
The pool-def attribute is used to define ip address pools for the above authorization call e.g.
user = foo {
login = clear lab
service = ppp { protocol = ip { set addr-pool=bbb } }
}
user = nas1-pools {
service = ppp {
protocol = ip {
set pool-def#1 = "aaa 1.0.0.1 1.0.0.3"
set pool-def#2 = "bbb 2.0.0.1 2.0.0.10"
set pool-def#3 = "ccc 3.0.0.1 3.0.0.20"
set pool-timeout = 60
}
}
}
In the example above is a configuration file fragment for defining 3 pools named aaa, bbb and ccc on the NAS named
nas1. When the user foo refers to the pool named bbb, if the pool bbb isn’t defined, the NAS will attempt to download the
definition contained in the nas1-pools entry. The other pools will also be defined at the same time (or they will be ignored
if they are already defined). Since this procedure is only activated when an undefined pool is referenced, one way to redefine a
pool once it has been downloaded is to manually delete the definition e.g. by logging into the NAS, enabling, and configuring:
config t
no ip local pool bbb
^Z
When a pool is deleted, there is no interruption in service for any user who is currently using a pool address. If a pool is
deleted and then subsequently redefined to include a pool address that was previously allocated, the new pool will pick up the
allocated address and track it as expected. Since downloaded pools do not appear in the NAS configuration, any downloaded
pool definitions automatically disappear whenever a NAS reboots. These pools are marked as "dynamic" when they appear
in the output of the show ip local pools NAS command. Since it is desirable not to have to manually delete pools to
redefine them, the AV pair pool-timeout=n can be used to timeout any downloaded pool definitions. The timeout n is in
minutes. The effect of the pool-timeout attribute is to start a timer when the pool definitions are downloaded. When the
timer expires, the pools are deleted. The next reference to a deleted pool via will cause a re-fetch of the pool definitions. This
allows pool changes to be made on the daemon and propagated to the NAS in a timely manner. See also:
sho ip local pool ...
ip local pool ...
• old-prompts (PPP/SLIP)
This attribute allows providers to make the prompts in TACACS+ appear identical to those of earlier systems (TACACS and
XTACACS). This will allow administrators to upgrade from TACACS/XTACACS to TACACS+ transparently to users. The
difference between the prompts is as follows: In XTACACS, when the user types slip or ppp the system prompts for an
address followed by a password, whereas TACACS+ prompts only for an address. In XTACACS, if the user types slip
host or ppp host, the system prompts for a password. In TACACS+, there is no prompt. Using this attribute, TACACS+
can be made to mimic the prompting behaviour of xtacacs, by configuring network authorization on IOS, and using the old-
prompts=true attribute value pair for slip and ppp/ip, viz:
user = joe {
service = shell { }
service = slip {
default attribute = permit
set old-prompts=true
}
service = ppp {
TACACS+ 77 / 79
protocol = ip {
default attribute = permit
set old-prompts=true
}
}
}
i.e. the prompts are controlled by the addition of the old-prompts=true attribute.
• max-links (PPP/multilink - Multilink parameter; 11.3)
This AV pair restricts the number of multilink bundle links that a user can have. The daemon side declaration is:
service=ppp { protocol=multilink { set max-links=n } }
int virtual-template X
multilink max-links n
show ppp multilink
debug multilink
int virtual-template X
multilink load-threshold n
debug multilink
• ppp-vj-slot-compression
Reserved for future use.
• link-compression
Reserved for future use.
• asyncmap
Reserved for future use.
• x25-addresses (PPP/VPDN)
Reserved for future use.
• frame-relay (PPP/VPDN)
Reserved for future use.
TACACS+ 78 / 79
There may be some caveats to consider if you’re using a previous version of the software:
• 16-08-2009: To use the MAVIS backend for users not defined in the local configuration file, user backend =mavis is
now required.
• 13-06-2010: LDAP functionality was merged into a single script, requiring the environmental variable LDAP_SERVER_T
YPE to be set to generic if you were using the mavis_tacplus_ldap_authonly.pl script, to tacplus_sch
ema for functionality equivalent to mavis_tacplus_ldap.pl, and to microsoft for mavis_tacplus_ads.pl
functionality.
11 Bugs
• There may still be some nasty bugs lurking in the code. Please contact the author via the "Event-Driven Servers" Google Group
at [email protected] or http://groups.google.com/group/event-driven-servers if you think you’ve found
one.
12 References
Please see the source for copyright and licensing information of individual files.
• The following applies if the software was compiled with OpenSSL support:
This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/).
This product includes cryptographic software written by Eric Young ([email protected]).
• MD4 algorithm:
The software uses the RSA Data Security, Inc. MD4 Message-Digest Algorithm.
• MD5 algorithm:
The software uses the RSA Data Security, Inc. MD5 Message-Digest Algorithm.
• If the software was compiled with PCRE (Perl Compatible Regular Expressions) support, the following applies:
Regular expression support is provided by the PCRE library package, which is open source software, written by Philip Hazel,
and copyright by the University of Cambridge, England. (ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/).
TACACS+ 79 / 79
• The original tac_plus code (which this software and considerable parts of the documentation are based on) is distributed
under the following license:
Copyright (c) 1995-1998 by Cisco systems, Inc.
Permission to use, copy, modify, and distribute this software for any purpose and without fee is hereby granted, provided that
this copyright and permission notice appear on all copies of the software and supporting documentation, the name of Cisco
Systems, Inc. not be used in advertising or publicity pertaining to distribution of the program without specific prior permission,
and notice be given in supporting documentation that modification, copying and distribution is by permission of Cisco Systems,
Inc.
Cisco Systems, Inc. makes no representations about the suitability of this software for any purpose. THIS SOFTWARE IS
PROVIDED ``AS IS” AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMI-
TATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
• The code written by Marc Huber is distributed under the following license:
Copyright (C) 1999-2015 Marc Huber ([email protected]). All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following dis-
claimer in the documentation and/or other materials provided with the distribution.
3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment:
This product includes software developed by Marc Huber ([email protected]).
Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments
normally appear.
THIS SOFTWARE IS PROVIDED ``AS IS” AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITS AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS IN-
TERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.