Using the University of Minnesota Cookie Authentication Mechanism by Christopher A. Bongaarts (comments, criticism welcome!) $Date: 2004/06/07 20:57:33 $ $Revision: 1.10 $ This document describes the new single-signon cookie authentication system for use at the University of Minnesota. While the examples used are specific to Netscape Enterprise Server 3.x or Apache 1.3.x running on UNIX platforms, it is hoped that the general ideas behind it are clear. CHANGES: * $Log: cookieauth.txt,v $ * Revision 1.10 2004/06/07 20:57:33 cab * Add user reported working configurations section, and Dale App's * report on SuSE Linux success. * * Revision 1.9 2004/05/19 21:00:01 cab * document new commands in module; update list of tested component * versions * * Revision 1.8 2001/09/20 22:02:09 cab * Minor updates to reflect 2.6 release. Remove RSA glue lib stuff (RSA * patent expired couple years ago now). * * Revision 1.7 2000/10/13 15:13:57 cab * Minor clarifications and corrections. * * Revision 1.6 2000/03/21 23:45:42 cab * Update to reflect the new API for the Central Auth Hub: instead of * using PATH_INFO to pass the return URL, it is passed explicitly as a * parameter in the QUERY_STRING. This avoids inconsistent handling of * PATH_INFO by Apache, and is a potential performance win as well (no * more need to do useless directory stat(2)'s all day). Overview ======== This is the general flow of events during a session for which authentication is required. First, the browser connects to the web server to establish the session. The web browser has no credentials to send to the server at this point, so the server redirects the browser to the Central Authentication Hub (CAH) service for validation. The user enters his/her username and password (or performs other validations to be added later if desired, including PKI or secure password validation). The CAH sets an encrypted cookie in the browser containing validation credentials. The cookie will be sent to all secure (HTTPS) servers in the umn.edu domain. After setting the cookie, the CAH redirects the client back to the server which originated the request. Now when the browser connects to the server, it sends its cookie along with the request. The server sends the encrypted cookie over an SSL connection to our X.500 validation service. The X.500 validation service decrypts the cookie and returns whatever data the application needs. The web server caches this information so that future requests from the browser need not trigger an X.500 lookup. Details ======= Redirecting for Authentication ------------------------------ If the browser does not have proper credentials (i.e. no cookie or a cookie whose timestamp is too old), it should redirect the browser to a URL of this form: https://www.umn.edu/login?desturl=https%3a%2f%2fmyservername .umn.edu%3a8443%2fserver_path%2ffile.html%3farg%3dvalue Embedded in the login URL is the URL that will be used to redirect the client back to the web server after authenticating. In this case, it would redirect back to: https://myservername.umn.edu:8443/server_path/file.html?arg=value You must escape the "desturl" parameter as you would any GET form argument: anything other than letters, numbers, or "-_.!~*'()" must be escaped. See RFC 2396 section 3.4 for details. Cookie Handling --------------- The server sends back a base-64 encoded cookie named "umnAuthV2" containing validation information: what type of validation was used, a timestamp, the IP address of the browser, and the user's X.500 username. The web server should extract the value of the umnAuthV2 cookie and send it over an SSL connection to the X.500 validation server, x500.umn.edu, port 87. What to send may vary by application; the most general request, which anyone may use, is: WEBCOOKIEcookie which returns either NO:reason or OK:30|919458627|160.94.190.130|bong0004| The fields, in order, are: * validation level * timestamp, UNIX format (seconds since midnight Jan 1, 1970 GMT) * IP address * username * always blank (was X.500 distinguished name in version 1) Contact the U Card office for a Data Access Form if you want to get other information. The validation level is an integer representing the relative strength of the validation mechanism used. Some applications may wish to restrict this to require a higher-security validation. However, most applications should be content to know that the user has validated somehow; they can ignore this field. Current values are: 20 = Account initiation 30 = Normal username/password 40 = Enterprise Systems password 50 = PKI Your application is responsible for verifying that the IP address given in the cookie matches the IP address of the browser (REMOTE_ADDR in CGI). Without this check, it is possible for unauthorized users to "steal" cookies from legitimate users and use them on another computer (e.g. the next computer over in a public lab). Also, your application may want to set a stricter time limit on the lifetime of a cookie than the limit imposed by the X.500 validation service (currently 3 hours). EXAMPLES ======== Both examples require OpenSSL 0.9.6. Earlier versions of OpenSSL or SSLeay may work with minimal modifications to the example source. The OpenSSL homepage is at http://www.openssl.org/ The file cookieauth-26.tar.gz contains these files: README This file nsapi/ Example NSAPI for Netscape Enterprise 3.x cookieauth.c NSAPI source code - NEEDS FIXES Makefile Makefile for NSAPI apache/ Example module for Apache 1.3.x mod_cookieauth.c module source code Makefile Makefile for Apache module To unpack the tar.gz file, use a command like this: gzip -cd cookieauth-26.tar.gz | tar xvf - This will create a directory called "cookieauth-26" in the current directory containing the above files. Example Apache Module ===================== The example Apache module requires mod_ssl built with the mm shared memory library. It has been tested (by me) on: Apache 1.3.26, mod_ssl 2.8.9, OpenSSL 0.9.7c, libmm 1.1.3 (Compiled on Solaris 8 with Sun Forte C 7.) Reports from other users: Dale App says: > I have successfully compiled Mr. Bongaarts cookieauth (version 2.7) > and have it working with the following configuration: > > SUSE Linux 9 > Kernel version 2.4.21-215 > Apache version 1.3.28-43 > mod_ssl version 2.8.15-60 > OpenSSL version 0.9.7b-133 > mm version 1.3.0-33 > > All modules were installed using the SUSE YAST2 allowing YAST2 to > set the directory structure. I only had to modify the Makefile to > allow for the changes in directory locations for the locations of > the SSL as outlined in Mr. Bongaarts README file. There are known bugs in earlier versions of mod_ssl that cause seg faults, and earlier versions of libmm had bugs that prevented proper shared memory locking. TIP: use the mod_ssl INSTALL file as your blueprint for installation order of these packages. Then you won't find yourself compiling things twice... Once Apache is installed, you can compile the module. Edit the provided Makefile, changing the paths for OpenSSL as necessary. The Makefile uses Apache's apxs to compile and/or install the module, so no other Makefile tweaking should be necessary. Run "make", then "make install" to install the module. Now you can configure your web server to protect paths on your server. Here are example lines, used to protect a hierarchy called "secret_docs": # these lines may be added automatically by make install LoadModule cookieauth_module libexec/mod_cookieauth.so AddModule mod_cookieauth.c # this is used by the shared memory library to cache responses # from the cookie validation server. CookieAuthCacheFile "logs/cookieauth.cache" # use my system's random device instead of the internal RNG CookieAuthRNG file /dev/urandom 1024 AuthType CookieAuth CookieAuth on require valid-user This configuration uses the default settings for the module. For most users these will be sufficient. The configurable settings are (from mod_cookieauth.c): # set hostname, port and "secure/insecure" (SSL/non-SSL) # communication with X.500 authentication host # if CookieAuthSSL is Off, then the port defaults to 77. CookieAuthHost x500.umn.edu CookieAuthPort 87 CookieAuthVerb WEBCOOKIE CookieAuthSSL On # cookies are invalid after this many seconds (default 3 hours) # setting this greater than 10840 (3 hours) is not useful, as the # validation service will not honor cookies older than that. CookieAuthTimeout 10840 # set this to the auth level you require # 99.9% of users come in at level 30, but you might want to # allow level 20 as well for students who have just initiated # their email account. # 20 = student account initiation # 30 = normal username/password # 40 = enterprise systems password (FFN, EGMS, etc.) # 50 = PKI CookieAuthLevel 20 # how long to cache cookies before revalidating with the # directory servers. leave it be unless you have a darn # good reason; lower values increase load on the servers, # and higher values decrease security. CoookieAuthCacheTimeout 600 # how to seed the random number generator. # first argument is method; second is filename; # third is # of bytes to use; # default: CookieAuthRNG internal # use EGD, the Entropy Gathering Daemon CookieAuthRNG egd /var/run/egd-pool # use file or device (e.g. /dev/random) CookieAuthRNG file /dev/urandom 1024 # restrict access to a set of usernames listed in a file. # one username per line. lines starting with # ignored. CookieAuthACLFile /var/www/acls/csci1001.acl # use this if you're behind a NAT-like load balancer or # portal to ignore the cookie's IP when coming # the following allows connections from the LB/portal # servers at 128.101.101.101 or 134.84.84.84 to get in # even if the cookie IP does not match. CookieAuthAllowAnyIP 128.101.101.101 134.84.84.84 Apache Module Technical Details ------------------------------- This section is intended for developers who won't be using the NSAPI, but will instead be implementing their own cookie-based authentication software. Apache modules are usually best read bottom-up. Read the functions in this order for best comprehension: cookieauth_authenticate, check_cookie_access, get_umnv2_cookie, verify_cookie, parse_umnv2_cookie cookieauth_authenticate is responsible for validating a cookie, if present, and storing its component parts in the request_rec structure. The username goes in r->connection->user and the auth level goes in r->connection->ap_auth_type; as a result, these values are available to other routines that access the username. For example, the username will be logged in the access log, both values will be available to CGI scripts in the usual REMOTE_USER and AUTH_TYPE variables, and other modules can restrict access based on the username (group files, require-auth, etc.) Other cookie variables are stored in the r->notes table. check_cookie_access does the work of assuring that the restrictions you make on validation level, timeouts, etc. are fulfilled. It is responsible for generating the redirect to the login page if authentication failed. Example NSAPI ============= NOTE: we no longer run Netscape Enterprise Server, so I have no means of continuing development on this. In particular, I cannot update it to use the new desturl= style of passing the return URL. Beware that if you use this, you should modify it to use the new format before the old format goes away! The example NSAPI requires Netscape Enterprise Server 3.x and OpenSSL 0.9.1c. It was last tested with Enterprise Server 3.0K and OpenSSL 0.9.1c and 0.9.4 on Solaris 2.5.1/2.6. Once OpenSSL is built and the Enterprise 3.x server is installed, you can compile the cookieauth NSAPI. Edit the Makefile provided, paying particular attention to the paths for the SSL and NSAPI include files and libraries, as well as the compiler options. The included Makefile was designed for Solaris 2.5.1/2.6 and the SPARCworks 4.2 C compiler; other operating systems and/or compilers probably require different options. Adding -DDEBUG to the CFLAGS will cause it to generate LOTS of debug output in the server error log for testing. Running "make" should produce a shared library called "cookieauth.so". At this point you can edit the server's obj.conf configuration file to protect paths on your server. Here are example lines, used to protect a hierarchy called "secret_docs": # near the beginning of the file, with other Init directives: Init fn="load-modules" shlib="/opt/Netscape/enterprise/nsapi-bin/cookieauth.so" funcs="cookieAuthInit,cookieAuthTr,cookiePathChk" Init fn="cookieAuthInit" # at the end of the file AuthTrans fn="cookieAuthTr" PathCheck fn="cookiePathChk" This configuration uses the default settings for the NSAPI. The configurable settings look like this: Init cookieAuthInit cache-timeout=600 The internal cookie-cache will be cleaned when a request comes in and the cache hasn't been cleaned for the number of seconds specified. When this occurs, all cookies that haven't been used since the last cookie cache cleaning will be discarded. AuthTrans fn=cookieAuthTr [host="x500.umn.edu"] [port=87] [secure=1] These parameters control how the X.500 validation service will be accessed. The validation service listens for SSL connections on port 87 and plain TCP connections on port 77. The "secure" setting selects whether to use SSL to make the connection. Normally you will not want to change these. PathCheck fn=cookiePathChk [minvaltype=20] [timeout=10840] The minvaltype parameter sets the minimum acceptable value for the validation type. If the client has not validated with a sufficiently strong mechanism, access will be denied. The timeout is used to reject cookies older than the given number of seconds. Setting this greater than 10840 (3 hours) is not useful, as the validation service will not honor cookies older than that. NSAPI Technical Details ----------------------- This section is intended for developers who won't be using the NSAPI, but will instead be implementing their own cookie-based authentication software. The cookieAuthInit routine is called once at server startup time. It initializes some variables and creates an SSL context that will later be used to create individual outgoing SSL connections to the validation service. cookieAuthTr is responsible for decoding the cookie and putting its parts into various "server variables" for use by cookiePathChk. This stage is where the cache is checked and the validation service is contacted if necessary. Either via the cache or the validation service, the result is place in "cookie_verified" and parsed to find the values of the individual fields. These are stored in the server's working variables (rq->vars). cookiePathChk makes the actual decision whether to accept the authentication or not. If no cookie fields are present in the server's working variables, then no cookie was presented, so the authentication fails. Likewise, if the "strict" checking fails, the cookie is judged to be too old, or the IP address does not match, the authentication will fail. In the event of failure, the client is redirected to the Central Auth Hub, with the URL requested as the embedded URL to come back to.