March 16, 2005

UMN::CookieAuth 0.1.4

Here's something I've written to imitate the University's apache cookieauth module using mod_perl. This is my first experiment with mod_perl, but it seems to work fairly well. Any comments are welcome.

Benefits:

  • You can use the included cookieauth_ctl script to browse the cookieauth shared memory cache to see what's being cached -- i.e. who's logged in.
  • You can set up your site to use local cookies which get their authentication from the central UMN authentication system, but thereafter don't grant access to any other UMN sites. Handy for dropping unneeded priviliges.
  • Easily hackable. (I mean: "easy to program extensions, etc." not "easy to crack"!)

Drawbacks

  • Probably slower than mod_cookieauth
  • Requires mod_perl

Download UMNCookieAuth-0.1.4.tgz

Here's the readme file:

NAME
    UMN::CookieAuth - Provides cookie authentication for apache 1.x via the
    University of Minnesota's central authentication system.

SYNOPSIS
     <Location /cookieauth>
       PerlAuthenHandler UMN::CookieAuth
       AuthType UMN::CookieAuth
       PerlAuthzHandler UMN::CookieAuth # optional if you're doing your own
                                        # authorization
       Require valid-user   # required lest apache
                            # skip the authentication
                            # phase altogether
       PerlSetVar <option name> "<option value>"
     </Location>

DESCRIPTION
    Provides cookie authentication against the University of Minnesota's
    cookieauth/x500 system for Apache 1.x using mod_perl. The authenticated
    user name will be placed in the Apache request object's "user" variable.

    Starting with version 0.1.4 the module will also do authorization if
    specified via PerlAuthzHandler and an ACL file is specified.

    This is beta software. It seems to work for me, but I wouldn't recommend
    deploying it anywhere without testing it yourself. This is my first
    attempt at doing something useful with mod_perl, so suggestions and/or
    criticism are welcome.

    This module is modeled after Chris Bongaarts' cookieauth module at
    http://www1.umn.edu/is/cookieauth/. Further documentation on the
    cookieauth system can be found there.

   Installation
    Place the UMN directory somewhere in perl's @INC path so mod_perl can
    find it. I use the "use lib" directive in a startup.pl script to add my
    modules directory to the path when running mod_perl, alternatively you
    could install it in system-wide perl module directory. If you want to
    use cookieauth_ctl, you'll need to place it in the parent directory of
    the UMN directory, install UMN::CookieAuth system-wide, or modify the
    script so it knows how to find the module.

    The first time the module is executed it will create a shared memory
    cache with permissions 0600. This cache will continue to exist even if
    you shut down apache. You can use the cookieauth_ctl script to browse
    the contents of the shared memory cache or ipcs and ipcrm to manipulate
    the shared memory. Note: to access the shared memory cookieauth_ctl will
    need to run as the web server user or root.

   Sequence of events
    If specified as a PerlAuthenHandler, UMN::CookieAuth will inspect each
    incoming request.

    First, if the request is plain http, the client is redirected to the
    secure port of the current server.

    If the request is over https, then we look for the authorization cookie.
    If the configuration specifies a local cookie (one set by us as
    described in "Local Cookies") then we'll look for that first and if
    unable to find it, fall back to the global cookie set by the central
    auth server. If no local cookie is specified we'll just look for the
    global cookie. If no cookies are found we'll redirect the client to the
    central auth login site.

    If we find a cookie we'll check in a cache in shared memory for it's
    decrypted value. If it's not already in the cache, we'll request it's
    decrypted value from the central auth server's X500 service and then
    store the decrypted value in the cache for the next request.

    If the cookie is invalid or expired, or the ip address does not match
    the client, or the authentication level is too low, then the client is
    redirected to the central auth login server.

    Otherwise if the cookie is good we set the apache user value to the user
    name retrieved from the decrypted cookie (which should be the user's UMN
    internet ID) and allow the client's request to proceed. If the
    configuration specifies to use a local cookie, there may be an
    additional redirect here simply to set the cookie with the remote
    browser.

    If the PerlAuthzHandler is specified, then we'll additionally look for
    the user name in an Access Control List file (see UMNCookieAuthACLFile).
    If the user is to be denied access we'll return a http forbidden
    response, otherwise access is granted and the request proceeds.

  CONFIGURATION OPTIONS
    Configuration options can be set in httpd.conf or .htaccess with the
    PerlSetVar directive. I've separated the options into two sections:
    "Things you may want to mess with" and "Things you probably don't want
    to mess with".

   Things you may want to mess with.
    UMNCookieAuthACLFile
        Default: "". Absolute path name to ACL file. The ACL file itself
        should be a series of line separated records in the form "allow
        {user_id}" or "deny {user_id}" (e.g. "allow cayfo001" or "deny
        cayfo001"). The special user_id value * matches all users. Upon
        finding a matching user_id the directive (allow or deny) is applied
        and the rest of the file is ignored. Falling off the end of the file
        without a match or failing to read the ACLFile both default to "deny
        *". Blank lines are ignored as is anything on a line following the
        comment character '#'. The ACLFile option is only applicable if you
        have specified UMN::CookieAuth as a PerlAuthzHandler, otherwise it
        will be ignored.

    UMNCookieAuthAllowAnyIP
        Default: "". Space or comma separated list of ip addresses for which
        we'll allow unmatched cookie ip addresses. Normally if the ip
        address encoded in the cookie does not match the remote client we
        reject the authentication and redirect them to the login site. If
        the remote client is coming through a load balancing proxy this
        could be a problem as their ip address would change.

    UMNCookieAuthDropUmnCookie
        Default: "never". Valid values are "never", "ifnew", and "always".
        See "Local Cookies".

    UMNCookieAuthHttpsPort
        Default: "443". HTTPS port for this server. If we get a plain http
        request we'll redirect to the current server using https on this
        port.

    UMNCookieAuthLevel
        Default: "30". U of M authentication level required to access the
        location. Values are 20 (new internet id password), 30 (standard
        internet id password), 40 (enterprise password), 50 (pgp)

    UMNCookieAuthLocalCookieName
        Default: "umnAuthV2Local". Name of the parallel cookie we may use
        for this local site. See "Local Cookies" below.

    UMNCookieAuthLogging
        Default: "1". Logging level, currently 0, 1, or 2 in increasing
        verbosity. Events should be reported in Apache's error.log.

    UMNCookieAuthMaxAge
        Default: "3600". Time in seconds until cookie expires. Retroactively
        calculated from the time the UM cookie was created.

    UMNCookieAuthMemoryKey
        Default: "UMCK". Four letter key for IPC shared memory cache. Once a
        cookie is decrypted, we store it in shared memory so all apache
        children can access it and save themselves a trip to the x500
        server. If you want to keep different servers separate you can use a
        different key here. Mostly useful for running a development version.
        If the module finds a shared memory cache at the specified key, but
        with a different cache version, it will increment the key and try
        again.

    UMNCookieAuthNewCookieLifetime
        Default: "300". Expiration time in seconds of the temporary cookie.

    UMNCookieAuthNewCookieName
        Default: "umnAuthTemp". Name of the one-shot, temporary cookie we
        use to track if this is a brand new login from the login server. See
        "Local Cookies" below.

    UMNCookieAuthUseLocalCookie
        Default: "never". Valid values are "never", "ifnew", and "always".
        See "Local Cookies".

   Things you probably don't want to mess with.
    These settings need to synchronize with the University's central
    authorization service so you probably won't want to tweak these unless
    that changes. Or if you've set up your own auth server.

    UMNCookieAuthCookieName
        Default: "umnAuthV2". Name of the authentication cookie doled out by
        the umn login server.

    UMNCookieAuthLoginSite
        Default: "https://www.umn.edu/login?desturl=%s". Site to redirect to
        for logins. The %s will be replaced by the url originally requested
        by the client.

    UMNCookieAuthQueryFormat
        Default: "WEBCOOKIE\t%s\n". How to format the query to the
        authentication server. The %s will be replaced by the encrypted
        cookie value. Currently recognized special characters are \t, \r,
        and \n. These should be spelled out as here, a literal \ followed by
        t, r, or n.

    UMNCookieAuthX500Host
        Default: "x500.umn.edu". X500 authentication server host. Used to
        decrypt the cookie value.

    UMNCookieAuthX500Port
        Default: "87". TCP port for x500 server.

  LOCAL COOKIES
    Local cookies are designed to allow a browser to access our site while
    dropping unneeded privileges to the rest of the university. Essentially
    we copy all the decrypted information from a valid global cookie into a
    cookie that we set ourselves and then--optionally--delete the global
    cookie. This leads to several configuration options based on the values
    of "UseLocalCookie" and "DropUmnCookie"

    If UseLocalCookie is set to "never" (the default) authentication will
    proceed as usual. The DropUmnCookie setting should have no effect in
    this case.

    If UseLocalCookie is set to "ifnew", then the module will look for the
    NewCookie, which should have been set during any redirection from here
    to the login site. If this NewCookie is found it is deleted and the
    LocalCookie is set instead with the same value and properties that are
    found in the primary UMN cookie. The local cookie will be used for
    authentication from then on. If this is not a new login, then
    authentication will revert to the UMN cookie.

    If UseLocalCookie is set to "always", then the local cookie will always
    be used even if we did not initiate the login process.

    If DropUmnCookie is set to "never" (the default) the UMN global cookie
    will not be affected. The remote user's browser will still hold a global
    cookie for the rest of the university.

    If DropUmnCookie is set to "ifnew", the UMN cookie will be deleted if we
    detect through the NewCookie that this is a new login initiated by us.
    This is probably the setting you want if you're using local cookies. If
    the remote user already had a global cookie (e.g. they logged into
    webmail before coming to our site), then they will still have global
    access after coming to our site.

    If DropUmnCookie is set to "always", the UMN cookie will be deleted
    regardless of how the authentication was initiated. This may cause some
    distress as any users will find themselves logged out of their
    pre-existing browser sessions after visiting our site.

  EXPORT
    None. This module uses the object oriented interface from mod_perl. If
    you want to use it without being under mod_perl (eg. to query the shared
    memory cache as the cookieauth_ctl script does) you can use
    UMN::CookieAuth->new() to give you a handler object, although anything
    that depends on mod_perl--such as setting cookies--will fail.

AUTHOR
    Steve Cayford <cayfo001 @ umn . edu>

REQUIRES
    Apache
    ssl
    mod_perl
    Apache::Constants
    Apache::Table
    Apache::URI
    Apache::Cookie
    Apache::Log
    CGI::Util
    IPC::ShareLite
    Storable
    Net::SSLeay

SEE ALSO
    *   cookieauth_ctl - Tool for examining the cookieauth shared memory
        cache. Needs work, though. Try 'cookieauth_ctl --help' for usage.

    *   Chris Bongaarts' apache module in C
        http://www1.umn.edu/is/cookieauth which inspired this perl
        version.

    *   mod_perl (http://apache.perl.org)

    *   perl (http://perl.org)

CHANGES
    0.1.4, 2005-03-14, by Steve
        Added ACLFile option and PerlAuthzHandler functionality. More
        documentation.

    0.1.3, 2005-02-01, by Steve
        Switched the whole module to an object. Improved logging. Added the
        whole local cookie concept, which meant rewriting the high level
        structure and probably introduced a bunch of new bugs. The shared
        memory cache is only cleaned based on a timer value, rather than
        every time through. Added AllowAnyIP option. Switched from
        Apache::Util::escape_uri() to CGI::Util::escape() -- not sure why
        Apache::Util wasn't working.

    0.1.2, 2004-12-30, by Steve
        Redirect to local https if http rather than straight to the login
        server.

    0.1.1, 2004-12-16, by Steve
        Pulled shared memory connection into a lexical variable reference in
        a function, added memory key config, added HttpsPort option, added
        documentation.

    0.1.0, 2004-10-28, by Steve
        Original working version.

BUGS
    Nothing serious that I'm aware of yet, but please contact me if you find
    anything.

TODO
    *   Perhaps a rolling timeout, where the timeout could be short (e.g. 5
        or 10 minutes), but each successful access resets the timeout. So
        this would timeout idle cookies, but not those making constant
        access. Along with this we'd probably need a timeout limit to force
        re-validation at some point.

    *   Do we need a separate CacheTimeout option to force re-validation of
        cookies as cookieauth does? I'm not sure what the security benefit
        is. This will need to use the LocalCookie value for the UMN cookie
        if necessary. And may require changing the clean_interval setting.

    *   Shared memory locking should be handled better. Need to use shared
        locks when possible.

    *   Should provide a "forbidden" page explaining the problem if
        credentials are too low or invalid IP rather than just redirecting
        to the login site.

    *   Should provide a logout method - especially for local cookies. Via
        the url? Perhaps just tack a ?UmnCookieAuthLogout on the end of the
        url. If we see this, delete the cookie(s) and redirect.

    *   Should there be an option to use a file as the cookie cache instead
        of shared memory?

    *   Suggestions?
Posted by cayfo001 at 4:49 PM