Note: this is a DEVELOPER'S KIT. You probably shouldn't be using this if you don't need source code. Instead, consider using CiscoSecure, Cisco's supported, commercial Tacacs+ daemon.
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 LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
Please NOTE: None of the TACACS code available here comes with any warranty or support, however, comments or questions may be addressed to Cisco systems via email at the address:
and we will do our best to handle them, though we cannot guarantee a timely response, as this code is UNSUPPORTED. Be sure you've read this user's guide, including the frequently asked questions include in it, before posting.
Cisco systems also maintains an extensive World Wide Web site at
In addition, there are two mailing lists which may be of interest to users of Tacacs+.
The first is a mailing list run by spot.Colorado.EDU which discusses many things pertaining to Cisco products. It is not run by Cisco Systems, Inc. and is not part of Cisco's formal service request channels, however, many knowledgeable people, including staff members of Cisco Systems, Inc. voluntarily read and respond on the list.
Requests to be added to or deleted from the list at spot.Colorado.EDU, along with other administrative issues concerning it can be sent to:
There is also a relatively new list called TACPLUS-L, run by disaster.com, created for the purpose of information exchange between TACACS+ Users. It is intended as a supplement to the list at spot.Colorado.EDU, aiding TACACS+ users and prospective users in many issues including but not limited to technical support, bug reports and workarounds, configuration information, recommendations for future versions of TACACS+, and general talk about TACACS+ development, implementation, administration, etc.
Please note that neither of these lists is in fact connected with Cisco Systems, Inc. or any of its subsidiaries. Standard etiquette rules apply.
To subscribe to the TACPLUS-L list, send a message to
In the body of the letter, enter
to be automatically added, or visit their web page at http://www.disaster.com/tacplus/.
Also, Robert Kiessling maintains a TACACS+ FAQ at http://www.easynet.de/tacacs-faq.
Lastly, I am always interested in seeing contributed patches, so consider mailing any modifications you make, as context diffs (be sure to indicate with the version your patches are based on), to tacacs-patches@cisco.com. As always, no support is implied, nor any assurance that patches will be made available via ftp (though that is my intent) or incorporated into any code.
Definitions and Terms
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".
TACACS, XTACACS and TACACS+
Note that there are now at least 3 versions of authentication protocol that people commonly 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.
The third one is TACACS+ (or T+ or tac_plus) which is what is documented here. TACACS+ is NOT COMPATIBLE with any previous versions of tacacs.
In addition to the 3 versions of tacacs running on Cisco boxes, the fact that we distribute the source code to the daemon has meant that additional implementations of tacacs daemons have been produced by people who have made modifications to our source code.
BUILDING TAC_PLUS
Tac_plus is known to build and run on the following platforms:
tar -xvf tac_plus.tarEdit the top of the Makefile to select the appropriate defaults for your system. Then type
make tac_plusThe default version can authenticate using its internal database, s/key or passwd(5) style files. Authorization is done via the internal database or via calls to external programs, which the administrator configures.
To use S/KEY, you must obtain and build the s/key library (libskey.a) yourself. You can then compile in S/KEY support per the instructions for S/KEY in the Makefile. I got my S/KEY code originally from crimelab.com but now it appears the only source is ftp.bellcore.com. I suggest you try a web search for s/key source code.
Note: S/KEY is a trademark of Bell Communications Research (Bellcore).
Should you need them, there are routines for accessing password files (getpwnam,setpwent,endpwent,setpwfile) in pw.c.
Lastly, you may also need to add lines to your /etc/services file to get tacacs+ to work correctly e.g. something along the lines of:
tacacs tcp/49You'll need to consult your system manuals for exact details of how to do this.
A NOTE ABOUT ARAP, MSCHAP AND DES
If you have access to a DES library which implements the calls:
int des_init(); void des_setkey(); void des_endes(); void des_done();then you can define DES in tac_plus.h and link tac_plus with your DES library. This is recommended, as it will allow you to process ARAP and MSCHAP requests on your daemon, which is more efficient, and also more secure than processing them on the NAS.
If you don't have access to des (which is U.S. export controlled), you can simply leave DES undefined in tac_plus.h. ARAP and MSCHAP authentication will still work, but it will be slightly less efficient, since the NAS will attempt to get the daemon to do the DES calculation before falling back to the alternative of calculating DES on the NAS. It's also slightly less secure, because if someone discovers your encryption key, they can then download ARAP and MSCHAP secrets from your daemon.
Note that this issue arises solely because U.S. government regulations currently make it difficult to export the source code for DES outside the US and Canada, which is why it is not included in this distribution.
Lastly, this limitation of MSCHAP, ARAP and DES has no bearing on the use of des passwords for regular logins. Regular logins also use DES but they do it via the "crypt" system call, which is usually found in a library on the Unix host where you compile your daemon.
There are additional restrictions on doing MSCHAP (see the FAQ later in this document).
CONFIGURING TAC_PLUS
Tac_plus is configured via a single configuration file. You can create a configuration file from scratch or, if you have passwd(5) and supplementary files from earlier versions of tacacs, you can convert these to configuration file format by running the supplied perl script convert.pl.
CONVERTING EXISTING PASSWD(5) FILES
To convert an existing passwd(5) file e.g. one used with an older version of tacacs, use the convert.pl perl script as follows:
convert.pl[-g] [ ]
CONFIGURING TAC_PLUS FROM SCRATCH
A configuration file consists of some top-level directives for setting defaults and for setting up the encryption key, followed by a declaration for each user and group you want to configure. Within each user or group declaration, there are declarations for authenticating and authorizing that user.
This is done using the statement
key = "your key here"NOTE: 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.
During debugging, it may be convenient to temporarily switch off encryption by not specifying any key. Be careful to remember to switch encryption back on again after you've finished debugging.
The current code does not support host-specific keys (left as an exercise to the reader).
On the NAS, you also need to configure the *same* key. Do this by issuing:
aaa new-model tacacs-server key
Users and groups are declared as follows. 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.
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 member = admin } group = admin { # group admin is a member of group staff member = staff } group = staff { # group staff is not a member of any group }
This recursive process occurs for lookups of expiration dates, for pap, arap and chap "secrets", and also for authorization parameters (see later).
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.
PAP, 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 = cleartext "arap password" chap = cleartext "chap password" pap = cleartext "inbound pap password" opap = cleartext "outbound pap password" login = des XQj4892fjk }The following assigns the user agnes a single password for all the above types of login (except outbound PAP):
user = agnes { global = cleartext "Agnes global password" }NOTE: you cannot use a global user password for outbound PAP. This is because outbound PAP is implemented by sending the password from the daemon to the NAS. This is a security issue if the TACACS+ key is ever compromised.
There are 3 ways to authenticate a user for login.
user = joe { member = admin # this is lily's encrypted DES password. It overrides the admin # group's password login = des XQkR21zMB0TDU } user = fred { # fred is a member of group admin. He inherits the group's password # as he does not have his one of his own. member = admin } group = admin { # group admin has a cleartext password which all members share # unless they have their own password defined login = cleartext foobar }If no password is needed for this user, this can be accomplished with the 'nopassword' option:
user = foo { login = nopassword }NOTE: The C program built from generate_passwd.c may be used to hand-generate encrypted passwords, or they may be taken from a Unix passwd (or shadow) file.
You can specify this behavior per-user, by naming a passwd(5) file in the password declaration (instead of giving a DES password), as follows:
user = fred { # look in file /etc/tac_plus_passwords to authenticate this user login = file /etc/tac_plus_passwords }
user = fred { login = skey }
At the top level of the configuration file, you can set the default authentication to use a passwd(5) file, viz:
default authentication = file /etc/passwdThe effect of this statement is that if a user does not appear in the configuration file, the daemon will attempt to authenticate the user using passwords from this file i.e. /etc/passwd in this example.
If you have passwd(5) files from previous versions of tacacs daemons, this facility allows you to authenticate using the passwd(5) from older versions of tacacs, while you migrate to using the new configuration file.
user = lol { expires = "MMM DD YYYY" password = cleartext "bite me" }will cause the user's passwords to become invalid, starting on the expiry date. The only valid date format is e.g. "Jan 1 1980". Case is NOT significant.
A expiry warning message is sent to the user when she logs in, starting at 14 days before the expiration date.
On expiry, the administrator must re-set the expiry date in the configuration file in order to grant continued access. Expiry applies to all password types except "file" passwords.
If passwd(5) files are being used for authentication, the "expires" field in the configuration file is not consulted. Instead, the daemon looks at the the "shell" field of the password file entry for a valid expiry date.
If Solaris shadow password files are used for authentication, the "expires" field in the configuration file is not consulted. The expiry field from the shadow password file (if it exists) is used as the expiration date.
aaa new-model aaa authentication login default tacacs+NOTE: As soon as you issue this command, you will no longer be able to create new logins to your NAS without a functioning tacacs+ daemon appropriately configured with usernames and password, so make sure you have this ready.
As a safety measure while setting up, we suggest you configure an enable secret and make it the last resort authentication method, so if your tacacs+ daemon fails to respond you will be able to use the NAS enable password to login. To do this, configure:
enable secret foo aaa authentication login default tacacs+ enableIf 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.
You can enable via tacacs+ e.g. by configuring on the NAS:
aaa authentication enable default tacacs+then whenever you attempt to enable, an authentication request is sent with the special username $enab
(Note: in order to be compatible with earlier versions of tacacs, when the requested enable level is 15, the daemon will also try the username $enable$ before trying username $enab15$).
For example, with the above declaration, in order to enable on the NAS, you need a user declaration like this one, on the daemon:
user = $enab15$ { login = cleartext "the enable password for level 15" }Note: Be aware that this does have the side effect that you now have a user named $enab15$ who can then login to your NAS if she knows the enable password.
Here is a similar declaration allowing users to enable to level 4:
user = $enab4$ { login = des bsoF4OivQCY8Q }
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.
Command authorization is configured by specifying a list of egrep-style regular expressions to match command arguments (see the supplied man page, regexp.3, for a full description of regular expressions) and an action which is "deny" or "permit".
The following configuration example permits user Fred to run the following commands:
telnet 131.108.13.All other commands are denied (by default).and telnet 128. .12.3 and show
user=fred { cmd = telnet { # permit specified telnets permit 131\.108\.13\.[0-9]+ permit 128\.[0-9]+\.12\.3 } cmd = show { # permit show commands permit .* } }NOTE: If an argument list you specify contains spaces or tabs, you must enclose it in double quotes.
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.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 cmd = telnet { # allow all fred's telnet commands except to 131.108.13.* deny 131\.108\.13\.[0-9]+ permit .* } }Note: Matches are not anchored, so "deny 131.108.13.[0-9]+" matches anywhere in the command. To anchor the match, use ^ at the beginning of the regular expression.
Note: 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.
default authentication = file /etc/passwd user = DEFAULT { service = ppp protocol = ip { addr-pool=foobar } }In this example, users who do not appear elsewhere will be authenticated via the /etc/passwd file, and authorized by the contents of the user = DEFAULT entry.
Note: For backward compatibility, the directive,
default authorization = permitmay still be specifed at the top level of the configuration file. This overrides the default denial of authorization for users who are not explicitly listed in the configuration file, permitting all authorization requests for such users.
user = lol { default service = permit }NOTE: This directive must appear first inside the user declaration.
user = lol { service = exec { 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.
In addition to agreeing to allow an exec to start, you can supply some parameters whenever an exec 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 { # this following line permits an exec to start and permits # all commands and services by default default service = permit service = exec { # When an exec is started, its connection access list will be 4. # It also has an autocmd. acl = 4 autocmd = "telnet foobar" } cmd = telnet { # allow all fred's telnet commands except telnet to 131.108.13.* deny 131\.108\.13\.[0-9]+ permit .* } }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 tacacs+
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. This is to signify 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.44You can use the NAS debugging command
debug aaa authorizationto 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.
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.
As you can see from the above, a program which plans to handle authorization must be able to handle a variety of requests and respond appropriately.
aaa authentication login default tacacs+ aaa authentication ppp default if-needed
aaa authentication ppp default tacacs+ int async 1 ppp authentication chapIf you omit either of these authentication schemes, you will start to see authorization requests in which the username is missing.
The complete algorithm by which the daemon processes its configured AV pairs against the list the NAS sends, is given below.
If the AV pair from the NAS is mandatory:
key = "your key here" user=fred { login = des mEX027bHtzTlQ name = "Fred Flintstone" member = administrators expires = "May 23 2005" arap = cleartext "Fred's arap secret" chap = cleartext "Fred's chap secret" service = exec { # When Fred starts an exec, his connection access list is 5 acl = 5 # We require this autocmd to be done at startup autocmd = "telnet foo" } # All commands except show system are denied for Fred cmd = show { # Fred can run the following show command permit system deny .* } service = ppp 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 addr=131.108.12.11 addr=131.108.12.12 addr=131.108.12.13 addr=131.108.12.14 # Fred's mandatory input access list number is 101 inacl=101 # We will suggest an output access list of 102, but the NAS may # choose to ignore or override it optional outacl=102 } service = slip { # Fred can run slip. When he does, he will have to use # these mandatory access lists inacl=101 outacl=102 } } user = wilma { # Wilma has no password of her own, but she's a group member so # she'll use the group password if there is one. Same for her # password expiry date member = admin } group = admin { # group members who don't have their own login password will be looked # up in /etc/passwd login = file /etc/passwd # group members who have no expiry date set will use this one expires = "Jan 1 1997" }
One solution is to arrange for the daemon to call your own user-supplied programs to control authorization. These "callouts" permit almost complete control over authorization, allowing you to read all the fields in the authorization packet sent by the NAS including all its AV pairs, and to set authorization status and send a new set of AV pairs to the NAS in response.
Fields from the authorization packet can be supplied to the programs you call on their command line, by using the appropriate dollar variables in the configuration file (see below).
AV pairs from the authorization packet are fed to the program's standard input, one per line. The program is expected to process the AV pairs and write them to its standard output, one per line. What happens then is determined by the exit status of the program.
NOTE: AV pairs are text strings with the format attribute=value. Unlike the configuration file which allows spaces when specifying AV pairs, there should be no spaces surrounding the "=" sign when using the programmatic interface.
user = auth1 { before authorization "/usr/local/bin/pre_authorize $user $port $address" }The AV pairs sent from the NAS will be supplied to this program's standard input, one pair per line.
Fields from the initiating authorization packet which the NAS sends to the daemon can also be passed to the program by using dollar variables in the command line. A complete list of available variables is as follows (consult the API specification for more details).
user -- user name name -- Nas name port -- Nas port address -- Nac address (remote user location) priv -- privilege level (a digit, 0 to 15) method -- (a digit, 1 to 4) type -- (a digit, 1 to 4) service -- (a digit, 1 to 7) status -- (pass, fail, error, unknown)Unrecognized variables will appear as the string "unknown".
If the program returns a status of 0, authorization is unconditionally permitted. No further processing is done on this request and no AV pairs are returned to the NAS.
If the program returns a status of 1, authorization is unconditionally denied. No further processing is done on this request and no AV pairs are returned to the NAS.
If the program returns a status of 2, authorization is permitted. The program is expected to modify the AV pairs that it receives on its standard input (or to create entirely new ones) and to write them, one per line, to its standard output. The new AV pairs will be sent to the NAS with a status of AUTHOR_STATUS_PASS_REPL. No further processing takes place on this request.
If the program returns a status of 3, authorization is denied, but all attributes returned by the program via stdout are returned to the NAS. Also, whatever the program returns on stderr is placed into the server-msg field and returned to the NAS as well.
Any other status value returned from the program will cause an error to be returned to the NAS.
Note that a status of 2 is not acceptable when doing command authorization.
The program can optionally modify the AV pairs being sent back to the NAS and change the authorization status if required.
group = auth1 { # call /usr/local/bin/post_authorize passing it the username, port # and current authorization status. after authorization "/usr/local/bin/post_authorize $user $port $status" }The AV pairs resulting from the authorization algorithm that the daemon proposes to return to the NAS, are supplied to the program on standard input, one AV pair per line, so they can be modified if required.
Fields from the incoming authorization packet which the NAS sent to the daemon can also be passed to the program on its command line by specifying dollar variables in the command line (see previous section).
The program is expected to process the AV pairs and write them to its standard output, one per line. What happens then is determined by the exit status of the program:
If the program returns a status of 0, authorization continues as if the program had never been called. Use this if e.g. you just want a program to send mail when an authorization occurs, without otherwise affecting normal authorization.
If the program returns a status of 1, authorization is unconditionally denied. No AV pairs are returned to the NAS. No further authorization processing occurs on this request.
If the program returns a status of 2, authorization is permitted and any AV pairs returned from the program on its standard output are sent to the NAS in place of any AV pairs that the daemon may have constructed.
Any other value will cause an error to be returned the the NAS by the daemon.
Pre and post authorization programs are invoked by handing the command line to the Bourne shell. On many Unix systems, if the shell doesn't find the specified program it returns a status of one, which denies authorization. However, at least one Unix system (BSDI) returns a status code of 2 under these circumstances, which will permit authorization, and probably isn't what you intended.
Note also that if your program hangs, the authorization will time out and return an error on the NAS, and you'll tie up a process slot on the daemon host, eventually running out of resources. There is no special code to detect this in the daemon.
Unless you make special arrangements, the daemon will run as root and hence the programs it invokes will also run as root, which is a security weakness. It is strongly recommended that you use absolute pathnames when specifying programs to execute, and that you use the Makefile options TAC_PLUS_USERID and TAC_PLUS_GROUPID so that the daemon is not running as root when calling these programs,
The daemon communicates with pre and post authorization programs over a pair of pipes. Programs using the standard i/o library will use full buffering in these circumstances. This shouldn't be a problem for most programs, since they'll read AV pairs till they see end of file on input, and they'll flush all output when they exit.
Note that when avpairs containing spaces are listed in the configuration file, you need to enclose them in double quotes so that they are parsed correctly. Avpairs which are returned via standard output do not need delimiters and so should not be enclosed in double quotes.
To configure command authorization on the NAS, issue the following NAS configuration commands:
aaa authorization commands 1 tacacs+ aaa authorization commands 15 tacacs+This will make the NAS send tacacs+ requests for all level 1 (ordinary user) and level 15 (privileged level) commands on all lines/interfaces.
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 tacacs+ if-authenticatedThis 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 login = des kppPfHq/j6gXs }
accounting file =Since accounting requests occur (and are serviced) asynchronously, it is necessary to lock the accounting file so that two writers don't simultaneously update it. The daemon uses the fcntl call to do this locking, so it is recommended that the accounting file reside on a local filesystem. Although fcntl locking over NFS is supported on some Unix implementations, it is notoriously unreliable. Even if your implementation is reliable, locking is likely to be extremely inefficient over NFS.
aaa accounting network stop-only tacacs+ aaa accounting exec stop-only tacacs+
Here is an example of AAA configuration (with exec and network accounting enabled):
NAS configuration:
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+ ! Example of AAA configuration for Exec: aaa authentication login execcheck tacacs+ aaa authorization network tacacs+ service exec-callback : line 4 login authentication execcheck ! Example of AAA configuration for ARAP: aaa authentication arap arapcheck tacacs+ aaa authorization network tacacs+ arap callback : line 4 arap authentication arapcheck ! Example of AAA-specific configuration for PPP callback: aaa new-model aaa authentication ppp pppcheck tacacs+ aaa authorization network tacacs+ : int async 6 ppp authentication chap pppcheck ppp callback acceptDaemon configuration:
Example of remote TACACS+ server CONFIG file entry for username `foobar':
user = foobar { arap = cleartext AAAA login = cleartext LLLL chap = cleartext CCCC pap = cleartext PPPP opap = cleartext OOOO service = ppp protocol = lcp { callback-dialstring=123456 } service = arap { callback-dialstring=2345678 } service = exec { callback-dialstring=3456789 callback-line=7 nocallback-verify=1 } }
tac_plus -P -Cwill syntax check the configuration file and print any error messages on the terminal.
For example, starting the daemon with
tac_plus -C CONFIG -d 16will put authentication debugging into /var/tmp/tac_plus.log. You can view this information by using the tail command.
tail -f /var/tmp/tac_plus.logSee the man page for more information.
On startup, tac_plus creates the file /etc/tac_plus.pid , if possible,
containing its process id. If you invoke the daemon so that it listens on a
non-standard port, the file created is /etc/tac_plus.pid. Assuming you are listening on the default port 49, something like the
following should work: NOTE: The perl script generate_passwd.pl may be used to hand-generate
encrypted passwords, or they may be taken from a Unix passwd file.
If you're willing to write the code, it should be a relatively simple
matter to interface the code to a database scheme e.g. unix dbm files, or
some proprietary database package, if you wish.
If we encrypted the CHAP and ARAP passwords in the database, then we'd
need to keep a key around so that the server can decrypt them when CHAP or
ARAP needs them. So this only ends up being a slight obfuscation and not
much more secure than the original scheme.
In extended TACACS, the CHAP and ARAP secrets were separated from the
password file because the password file may be a system password file and
hence world readable. But with TACACS+'s native database, there is no such
requirement, so we think the best solution is to read-protect the files.
Note that this is the same problem that a kerberos server has. If your
security is compromised on the kerberos server, then your database is wide
open. Kerberos does encrypt the database, but if you want your server to
automatically restart, then you end up having to "kstash" the key in a file
anyway and you're back to the same security problem.
So storing the cleartext password on the security server is really an
absolute requirement of the CHAP and ARAP protocols, not something imposed
by TACACS+.
We could have chosen a scheme where the NAS sends the challenge
information to the TACACS+ daemon and the daemon uses the cleartext password
to generate the response and returns that, but that means that we must
include specific protocol knowledge into the protocol for both ARAP and CHAP
and we would have to update the protocol every time a new authentication
protocol is added. Hence we decided to go with the SENDPASS mechanism.
Note that the above doesn't apply to PAP. You can keep an inbound PAP
password des-encrypted, since all you need to do with it is verify that the
password the principal gave you is correct.
For exec startup, it looks for a "service=exec" OR any command
configured.
For ppp, it looks for a "service=ppp" and "protocol=(one of lcp, ip,
ipx)".
For slip there must be a "service=slip" and for arap a "service=arap"
clause.
For specific commands, there must be a matching cmd= If these aren't found, authorization will fail, *unless* you say "default
service = permit".
For outbound PAP, where you are forced to send a password to the remote
host to identify yourself, there is now a separate "opap" directive e.g. NOTE: 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
does not apply to outbound PAP, only to inbound PAP, bidirectional CHAP and
ARAP.
Before 3.0, PAP logins were treated like ordinary user logins, so you
needed to declare a user in the Daemon configuration file whose name was
typically the remote hostname (or user), with a login password, in order to
process the PAP request.
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: 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.
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.
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 authorization.
The second issue is speed: Using config files containing 75,000 user
entries, I'm seeing about 3 authentications per second on a sparc 2 without
noticeable performance impact, though I haven't benchmarked this formally.
So more than about 3 authentications per second on this platform will
result in users seeing delays and having to wait for prompts. The usual
solution to this is to add more daemons to spread the load out.
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.
Then, the API spec states that the username in the identity structure on
the daemon is 32 bytes long, so only the first 31 bytes of username will be
copied from the authentication packet into this structure, which is then
null-terminated.
The password, on the other hand, is copied into malloc'ed memory, so it
can still be up to 255 characters long.
Now if it's a des encrypted password, then only the first 8 bytes are
significant, per the common unix implementations of crypt.
Lastly, there is also the question of how long a username/password can be
configured in the daemon configuration file. The answer is given by the
value of MAX_INPUT_LINE_LEN, currently set to 255, which determines the
length of the longest string you can enter in the configuration file.
timestamp, NAS name, username, port, address, record type.
Following these, a variable number of fields are written, depending on
the accounting record type. All are of the form attribute=value. There will
always be a task_id field.
Current attributes are:
Example records are thus: Maximum sessions are configured on the daemon for a user as follows: As the restriction is enforced during the authorization phase of login,
exec and network authorization must be configured as well, viz: If the result of finger indicates that the daemon should permit access,
access will be granted. Note that for this check to work via finger,
"service finger" must also be configured on the NAS.
Lastly, note that because finger output truncates usernames at 10
characters, you may encounter trouble if you have users whose names are not
unique within those first 10 characters.
Also recall that authorization works differently on the console. So many
people locked themselves out of their boxes after configuring authorization,
that we stopped requiring authorization on the console for authenticated
users. Since there's no authorization on the console, MAXSESS is not
enforced there.
There are currently no settable timeouts for PPP or SLIP sessions, but
there is a workaround which applies to ASYNC PPP/SLIP idle timeouts started
via exec sessions only: This workaround is to set an EXEC (idletime) timeout
on an exec session which is later used to start up PPP or SLIP (either via a
T+ autocommand or via the user explicitly invoking PPP or SLIP). In this
case, the exec idle timeout will correctly terminate an idle PPP or SLIP
session. Note that this workaround cannot be used for sessions which
autoselect PPP or SLIP.
An idle timeout terminates a connection when the interface is idle for a
given period of time (this is equivalent to the "session-timeout" Cisco IOS
configuration directive). The other timeouts are absolute. Of course, any
timeouts set by Tacacs+ apply only to the current connection. Note also that you cannot use the authorization "if-authenticated" option
with these parameters, since that skips authorization if the user has
successfully authenticated.
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 foo@bar.com, 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 T+ 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":
Example: Beginning with release 11.2(1.4), the additional configuration NAS: Like ARAP, MSCHAP works best when you have a des library (see the note at
the beginning of this document), but this is optional, as the DES
calculation can be done by the NAS instead. It also optionally requires a
key from Microsoft which is not public domain, but this can also be done on
the NAS too, so you can live without it.
To compile the daemon with MSCHAP support, uncomment the MSCHAP line in
the Makefile. This will add the MSCHAP code to your daemon. You can leave
MSCHAP_DES undefined, in which case the MSCHAP DES calculation will be done
by NAS, and no special MSCHAP key will be required.
If you have a DES library and want to use it with MSCHAP (this is more
efficient for authentication), then you can also uncomment the MSCHAP_DES
and MSCHAP_MD4_SRC lines in the Makefile. This will ensure that the DES
calculation done in the daemon. A key will need to be obtained from
Microsoft and used to replace the definition of MSCHAP_KEY in the file
mschap.h. If you're thinking by now that this is all too much trouble, I
can't say I blame you....
wtmp records are generated into wtmpfilename. Since file locking is also
used when writing to wtmpfilename, the usual provisos regarding NFS and
locking (see accounting above) should be observed.
To generate correct wtmp log records, the NAS needs to be configured as
follows: DAEMON: The "none" keyword in the NAS configuration line means that if the
tacacs+ server dies, any command will be allowed.
DAEMON: DAEMON: NAS: The following AV pairs specify which service is being authorized. They
are typically accompanied by protocol AV pairs and other, additional pairs
from the lists below.
# kill -USR1 `cat /etc/tac_plus.pid`
It's a good idea to check that the daemon is still running after
sending it a SIGUSR1, since a syntactically incorrect configuration file
will cause the daemon to die.
opap = cleartext OOOO
You use this to set the outbound PAP password. It must be a cleartext
password.
access-list 12 permit 192.85.55.0 0.0.0.255
line 1
access-class 12 out
Note: you must define "access-list 12 permit 192.85.55.0 0.0.0.255" on
the NAS. Only then can you use the acl avpair to apply it to a line that a
user dials in on.
aaa authorization exec local if-authenticated
This will use the local DB entry if one exists, allow authenticated
users otherwise, or fail.
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 expect more will be added over time.
Thu Jul 13 13:35:28 1995 cherub.cisco.com chein tty5 171.69.1.141 stop task_id=12028 service=exec port=5 elapsed_time=875
Thu Jul 13 13:37:04 1995 cherub.cisco.com lol tty18 171.69.1.129 stop task_id=11613 service=exec port=18 service=exec port=18 elapsed_time=909
Thu Jul 13 14:09:02 1995 cherub.cisco.com billw tty18 171.69.1.152 start task_id=17150 service=exec port=18
Thu Jul 13 14:09:02 1995 cherub.cisco.com billw tty18 171.69.1.152 start task_id=17150 service=exec port=18 service=exec port=18
Elapsed time is in seconds, and is the field most people are usually
interested in.
user = joeslip {
login = cleartext XXX
# only allow two sessions max for joeslip
maxsess = 2
name = "Joe SLIP User"
...
}
It can also be configured under a group: group = slip_users {
maxsess = 2
...
}
...
user = fred {
...
member = slip_users
...
}
The daemon keeps a count of how many sessions a given user owns by
monitoring START and STOP accounting records. Thus, exec and network
accounting must be configured for this feature to operate for exec and ppp
users.
aaa authentication login default tacacs+
aaa authentication ppp default tacacs+
aaa authorization exec tacacs+
aaa authorization network tacacs+
aaa accounting exec start-stop tacacs+
aaa accounting network start-stop tacacs+
Due to network outages (or other disruptions), it is possible for the
TACACS+ daemon's record of usage to become out of sync with reality, so
before denying access because it thinks a user is running too many sessions,
the TACACS+ daemon will use the finger service on the NAS to verify how many
sessions a user is running there.
user = lol {
login = cleartext foobar
service = exec {
# disconnect lol if there is no traffic for 5 minutes
idletime = 5
# disconnect lol unconditionally after one hour
timeout = 60
}
You also need to configure exec authorization on the NAS for the above
timeouts, e.g. aaa authorization exec tacacs+
Note that these timeouts only work for async lines, not for ISDN
currently.
username = domain {
service = ppp protocol = vpdn {
tunnel-id =
In addition the T+ 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.
user = foobar.cisco.com {
service = ppp protocol = vpdn {
tunnel-id = my_nas
ip-addresses = "173.20.12.19 173.20.12.20"
source-ip = 173.5.10.1
}
}
user = my_nas {
global = cleartext egad
}
user = my_home_gateway {
global = cleartext wowser
}
vpdn enable
aaa new-model
aaa authorization network tacacs+ ...
In addition, if the passwords for the home gateway and NAS are stored
on the T+ daemon, the command aaa authentication login tacacs+ ....
should also be present.
vpdn outgoing cisco.com ip NAS [ source-ip X.X.X.X ]
can be used. This changes in 11.2(4.0.2) and becomes: vpdn source-ip X.X.X.X
vpdn outgoing cisco.com ip NAS
ip local pool foo 1.0.0.1 1.0.0.10
Daemon: user = lol {
service = ppp protocol = ip {
addr-pool = foo
}
}
aaa accounting exec stop-only tacacs+
aaa accounting network stop-only tacacs+
aaa accounting system start-stop tacacs+
key =
NAS: aaa new-model
enable secret foobar
! use tacacs+. If server dies, use the enable secret password
aaa authentication login default tacacs+ enable
tacacs-server host
key =
NAS: aaa new-model
! all level 15 (privileged commands). If server dies, allow everything
aaa authorization commands 15 tacacs+ none
tacacs-server host
key =
NAS: aaa new-model
! authenticate exec logins (if not autoselecting)
aaa authentication login default tacacs+
! authorize network services via tacacs+
aaa authorization network tacacs+
! use tacacs+ for authenticating ppp users
aaa authentication ppp default tacacs+
tacacs-server host
aaa new-model
aaa authentication arap default guest tacacs+
aaa authorization network tacacs+
aaa accounting network start-stop tacacs+
!
appletalk routing
arap network
Daemon: key = "some key"
user = lol {
arap = cleartext
user = jim {
login = cleartext lab
service = raccess {
port#1 = nasname1/tty2
port#2 = nasname2/tty5
}
}
Requires IOS configuration aaa authorization reverse-access tacacs+
See the IOS docs for more details.
If cmd is
non-null, this is a command authorization request, It contains the name of
the command being authorized e.g. cmd=telnet
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.
line
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.
Note: For IPX,
inacl is only valid after 11.2(4)F.
inacl#1="permit ip any any precedence immediate" inacl#2="deny igrp
0.0.1.2 255.255.0.0 any"
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:
outacl#1="permit ip any any
precedence immediate" outacl#2="deny igrp 0.0.9.10 255.255.0.0 any"
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:
Note: in
10.3 this attribute was priv_lvl i.e. it contained an underscore instead
of a hyphen.
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 {
addr-pool=foo
}
}
During network authorization, the "route"
attribute may be used to specify a per-user static route, to be installed
via Tacacs+.
The daemon side declaration is: service=ppp protocol=ip {
route="
This indicates a temporary static route that is to be applied.
"
If gateway is omitted, the
peer's address is taken to be the gateway.
The route is expunged once
the connection terminates.
route#1="3.0.0.0 255.0.0.0 1.2.3.4"
route#2="4.0.0.0 255.0.0.0"
or, for IPX, route#1="4C000000 ff000000 30.12.3.4"
route#2="5C000000 ff000000 30.12.3.5"
See also:
In 11.2(5.4)F, this attribute was extended as follows:
ip-addresses = 172.21.9.26,172.21.9.15,172.21.9.4
The '/' 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 avpair: ip-addresses = "172.21.9.26 / 172.21.9.15 / 172.21.9.4"
172.21.9.26 is considered to be priority 1
172.21.9.15 is
considered to be priority 2
172.21.9.4 is considered to be priority
3
The NAS will try to forward the users to 172.21.9.26, before
trying 172.21.9.15. If the NAS can't forward users to 172.21.9.26, it
will try 172.21.9.15 next. If it fails with 172.21.9.15, it will then
try forwarding to 172.21.9.4.
For IP, both standard and extended Cisco access list
syntax is recognised, but it is an error to mix the two within a given
access-list.
For IPX, only Cisco extended access list syntax is legal.
Attributes are sorted numerically before being applied. For IP, the
first attribute must contain the name of a routing process and its
identifier (except for rip, where no identifier is needed), e.g. rte-fltr-in#0="router igrp 60"
rte-fltr-in#1="permit 0.0.3.4 255.255.0.0"
rte-fltr-in#2="deny any"
For IPX, no routing process is needed, e.g. rte-fltr-in#1="deny 3C01.0000.0000.0001"
rte-fltr-in#2="deny 4C01.0000.0000.0002"
See also:
Related IOS commands: IP: router
For
IP, both standard and extended Cisco access list syntax is recognised, but
it is an error to mix the two within a given access-list.
Attributes
are sorted numerically before being applied. The first attribute must
contain the name of a routing process and its identifier (except for rip,
where no identifier is needed), e.g. rte-fltr-out#0="router igrp 60"
rte-fltr-out#3="permit 0.0.5.6 255.255.0.0"
rte-fltr-out#4="permit any"
For IPX, no routing process is specified, e.g. rte-fltr-out#1="deny 3C01.0000.0000.0001"
rte-fltr-out#2="deny 4C01.0000.0000.0002"
See also:
Related IOS commands: IP: router sap#1="4 CE1-LAB 1234.0000.0000.0001 451 4"
sap#2="5 CE3-LAB 2345.0000.0000.0001 452 5"
The syntax of static saps is the same as that used by the IOS "ipx
sap" command.
See also:
Related IOS commands: [no] ipx sap ....
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"
or, for IPX, route#1="4C000000 ff000000 30.12.3.4"
route#2="5C000000 ff000000 30.12.3.5"
See also:
Only
Cisco extended access list syntax is legal, e.g sap-fltr-in#1="deny 6C01.0000.0000.0001"
sap-fltr-in#2="permit -1"
Attributes are sorted numerically before being applied. sho ipx access-lists
sho ipx interface
debug aaa author
debug aaa per-user
[no] ipx input-sap-filter
Only
Cisco extended access list syntax is legal, e.g sap-fltr-out#1="deny 6C01.0000.0000.0001"
sap-fltr-out#2="permit -1"
Attributes are sorted numerically before being applied. sho ipx access-lists
sho ipx interface
debug aaa author
debug aaa per-user
[no] ipx output-sap-filter
During IPCP address negotiation, if an ip
pool name is specified for a user (see the addr-pool attribute), a check
is made to see if the named pool is defined on the NAS. If it is, the pool
is consulted for an ip address.
If the required pool is not present on
the NAS (either in the local config, or as a result of a previous download
operation), then an authorization call to obtain it is attempted, using
the special username:
where
Note: This
username can be changed using the IOS configuration directive e.g. aaa configuration config-name nas1-pools-definition.cisco.us
The pool-def attribute is used to define ip address pools for the
above authorization call e.g. user = foo {
login = cleartext lab
service = ppp protocol = ip {
addr-pool=bbb
}
}
user = nas1-pools {
service = ppp protocol = ip {
pool-def#1 = "aaa 1.0.0.1 1.0.0.3"
pool-def#2 = "bbb 2.0.0.1 2.0.0.10"
pool-def#3 = "ccc 3.0.0.1 3.0.0.20"
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=
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:
IOS commands: ip local pool
11.2(7.4)P 11.2(7.4) 11.1(13.1) 11.(16.2) 11.1(13.1)AA
11.1(13.1)CA 11.1(13.1)IA 11.2(8.0.1)F 11.0(16.2)BT)
This attribute allows providers to make the prompts in T+ appear
identical to those of earlier systems (tacacs and xtacacs). This will
allow administrators to upgrade from tacacs/xtacacs to T+ 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 T+ prompts only for an address.
In xtacacs, if the user types "slip host" or "ppp host", the system
prompts for a password. In T+, there is no prompt.
Using this
attribute, T+ 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 {
global = cleartext foo
service = exec {
}
service = slip {
default attribute = permit
old-prompts=true
}
service = ppp protocol = ip {
default attribute = permit
old-prompts=true
}
}
i.e. the prompts are controlled by the addition of the
"old-prompts=true" attribute.
The daemon side declaration is: service=ppp protocol=multilink {
max-links=
The range of
Related NAS commands:
The daemon side declaration is: service=ppp protocol=multilink {
load-threshold=
The range of
Related NAS commands: