
/* ====================================================================
 * Copyright (c) 1996 The Apache Group.  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 disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgment:
 *    "This product includes software developed by the Apache Group
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
 *
 * 4. The names "Apache Server" and "Apache Group" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission.
 *
 * 5. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by the Apache Group
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
 *
 * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``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 THE APACHE GROUP OR
 * IT'S CONTRIBUTORS 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 INTERRUPTION)
 * 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.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Group and was originally based
 * on public domain software written at the National Center for
 * Supercomputing Applications, University of Illinois, Urbana-Champaign.
 * For more information on the Apache Group and the Apache HTTP server
 * project, please see <http://www.apache.org/>.
 *
 */

#include "httpd.h"
#include "http_config.h"

/*
 * cookieName is the name of the cookie we look for to convert to an
 * Authorization header.
 *
 * The config command AuthCookieName sets this value.  If it is not set,
 * we do no Cookie->Authorization conversion.
 *
 * cookieOverrides determines if the cookie value takes precedence over a
 * real Authorization header.  If the AuthCookieOverride config option is
 * "On", then we always use the Cookie version if available.  If it is "Off"
 * then we let any existing "Authorization" header remain unaltered.  The
 * default is "Off" if it is not specified.  If you turn this function on,
 * then you better make sure the password in the cookie matches that in your
 * database or your users will be seriously confused why they can't get in
 * even after properly typing their password.
 *
 * We assume the Cookie header comes to us as UserID:password with special
 * characters escaped using the standard %xx escapes.  Notably, spaces
 * and semicolons need to be escaped.  It doesn't hurt to escape all of
 * the characters, as this helps obscure the meaning of the Cookie to
 * the casual observer.  It is up to the web site to initially set this
 * cookie in the user's browser.  A good place to do that is when the
 * password is assigned.
 *
 * In order for this clever trick to work, we must ensure that this code
 * runs prior to any other authorization module.  To do this, the config
 * line for this module must appear below any other auth module in the
 * Apache Configuration file.
 *
 * $Id: mod_auth_cookie.c,v 1.6 1996/12/19 19:25:05 khera Exp $
 */

typedef struct {
    char *cookieName;
    int cookieOverrides;
} cookie_auth_config_rec;

static void *create_cookie_auth_dir_config(pool * p, char *d)
{
    cookie_auth_config_rec *m = ap_pcalloc(p, sizeof(cookie_auth_config_rec));
    if (!m)
        return NULL;            /* failure to get memory is a bad thing */

    m->cookieName = NULL;
    m->cookieOverrides = 0;
    return (void *)m;
}

static const char *set_override(cmd_parms *cmd, void *mrec, int arg)
{
    ((cookie_auth_config_rec *)mrec)->cookieOverrides = arg;
    return NULL;
}

static command_rec cookie_auth_cmds[] = {
    { "AuthCookieName", ap_set_string_slot,
      (void *) XtOffsetOf(cookie_auth_config_rec, cookieName),
      OR_AUTHCFG, TAKE1, "Name of cookie to convert to Authorization record" },
    { "AuthCookieOverride", set_override, NULL,
      OR_AUTHCFG, FLAG, "Cookie Auth overrides real Authorization header if On" },
    { NULL }
};

module cookie_auth_module;

/*
 * This uuencode function and the idea for faking the basic authentication
 * was taken from the apache_ssl.c code by Ben Laurie and then modified for
 * use here.
 */

static const char six2pr[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

/* return the string in uuencoded form */
static char *
     uuencode(pool * p, const char *szFrom)
{
    const unsigned char *s;
    int rlen = (strlen(szFrom) + 1) * 4 / 3 + 3;
    char *result = ap_palloc(p, rlen);
    char *szTo = result;

    for (s = (const unsigned char *) szFrom; *s; s += 3) {
        *szTo++ = six2pr[s[0] >> 2];
        *szTo++ = six2pr[(s[0] << 4 | s[1] >> 4) & 0x3f];
        if (!s[0])
            break;
        *szTo++ = six2pr[(s[1] << 2 | s[2] >> 6) & 0x3f];
        if (!s[1])
            break;
        *szTo++ = six2pr[s[2] & 0x3f];
        if (!s[2])
            break;
    }
    *szTo++ = '\0';
#if DEBUG
    fprintf(stderr, "UUencoded `%s' as `%s'\n", szFrom, result);
#endif
    return result;
}

/*
 * this doesn't really authenticate the user, but creates a fake
 * Authorization header based on the provided cookie
 */
static int cookie_authenticate_basic_user(request_rec * r)
{
    cookie_auth_config_rec *sec =
    (cookie_auth_config_rec *) ap_get_module_config(r->per_dir_config,
                                                 &cookie_auth_module);
    const char *cookie;

    if (!sec->cookieName)
        return DECLINED;        /* we're not configured */
    if (r->connection->user)
        return DECLINED;        /* too late for us to run */

    /*
     * if user supplied Authorization info, let it take precedence unless
     * we are overriding it.  this is potentially confusing to users to
     * override it.
     */
    if (!sec->cookieOverrides && ap_table_get(r->headers_in, "Authorization"))
        return DECLINED;

    /*
     * now check if there is a cookie set by the name specified.  if so,
     * then convert it into an Authorization header.
     */

    if ((cookie = ap_table_get(r->headers_in, "Cookie"))) {
        char buf[MAX_STRING_LEN];
        char *value = strstr(cookie, sec->cookieName);
        if (!value)             /* our cookie was not found */
            return DECLINED;
        value += strlen(sec->cookieName) + 1;   /* get past the "=" sign */
        strncpy(buf, value, MAX_STRING_LEN - 1);
        buf[MAX_STRING_LEN - 1] = '\0';
        value = strchr(buf, ';');
        if (value)
            *value = '\0';      /* Ignore anything after a ; */
        ap_unescape_url(buf);      /* assume comes in with %xx escapes */
        value = ap_pstrcat(r->pool, "Basic ", uuencode(r->pool, buf), NULL);
        ap_table_set(r->headers_in, "Authorization", value);
#if DEBUG
        fprintf(stderr, "AuthCookie set `%s'\nas `%s'\n", buf, value);
#endif
    }

    return DECLINED;            /* let the "real" authentication happen */
}

module cookie_auth_module =
{
    STANDARD_MODULE_STUFF,
    NULL,                       /* initializer */
    create_cookie_auth_dir_config,      /* dir config creater */
    NULL,                       /* dir merger --- default is to override */
    NULL,                       /* server config */
    NULL,                       /* merge server config */
    cookie_auth_cmds,           /* command table */
    NULL,                       /* handlers */
    NULL,                       /* filename translation */
    cookie_authenticate_basic_user,     /* check_user_id */
    NULL,                       /* check auth */
    NULL,                       /* check access */
    NULL,                       /* type_checker */
    NULL,                       /* fixups */
    NULL                        /* logger */
};
