479 lines
17 KiB
C
479 lines
17 KiB
C
|
/***********************************************************************
|
||
|
*
|
||
|
* Copyright (C) 2006 Novell, Inc. All Rights Reserved.
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Lesser General Public
|
||
|
* License as published by the Free Software Foundation; version 2.1
|
||
|
* of the License.
|
||
|
*
|
||
|
* This library is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Library Lesser General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General Public
|
||
|
* License along with this library; if not, Novell, Inc.
|
||
|
*
|
||
|
* To contact Novell about this file by physical or electronic mail,
|
||
|
* you may find current contact information at www.novell.com.
|
||
|
*
|
||
|
* Author: Juan Carlos Luciani <jluciani@novell.com>
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
|
||
|
#include <stdbool.h>
|
||
|
|
||
|
#include "apr_strings.h"
|
||
|
#include "ap_config.h"
|
||
|
#include "ap_provider.h"
|
||
|
#include "httpd.h"
|
||
|
#include "http_config.h"
|
||
|
#include "http_core.h"
|
||
|
#include "http_log.h"
|
||
|
#include "http_protocol.h"
|
||
|
#include "http_request.h"
|
||
|
|
||
|
#include "mod_auth.h"
|
||
|
|
||
|
#include "casa_s_authtoken.h"
|
||
|
|
||
|
//
|
||
|
// Module per-dir configuration structure.
|
||
|
//
|
||
|
typedef struct _authn_casa_dir_cfg
|
||
|
{
|
||
|
int performUsernameCheck;
|
||
|
|
||
|
} authn_casa_dir_cfg;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Forward declaration of our module structure.
|
||
|
//
|
||
|
module AP_MODULE_DECLARE_DATA authn_casa_module;
|
||
|
|
||
|
//
|
||
|
// Environment variables set by module
|
||
|
//
|
||
|
static char CasaIdentityIdEnvVar[] = "IdentityId";
|
||
|
static char CasaIdentitySourceNameEnvVar[] = "IdentityDataSourceName";
|
||
|
static char CasaIdentitySourceUrlEnvVar[] = "IdentityDataSourceUrl";
|
||
|
|
||
|
//
|
||
|
// Function: create_per_dir_config()
|
||
|
//
|
||
|
// Create per-dir configuration structure.
|
||
|
//
|
||
|
static void*
|
||
|
create_per_dir_config(
|
||
|
apr_pool_t *p,
|
||
|
char *x)
|
||
|
{
|
||
|
authn_casa_dir_cfg *pDirConfig;
|
||
|
|
||
|
// Allocate space for our configuration structure
|
||
|
pDirConfig = (authn_casa_dir_cfg*) apr_palloc(p, sizeof(*pDirConfig));
|
||
|
|
||
|
// Return our new configuration structure
|
||
|
return (void*) pDirConfig;
|
||
|
}
|
||
|
|
||
|
/* ************************************************************************
|
||
|
* set_authn_casa_uname_check()
|
||
|
*
|
||
|
* Process UsernameCheck configuration directive..
|
||
|
*
|
||
|
* L2
|
||
|
* ************************************************************************/
|
||
|
static const char*
|
||
|
set_authn_casa_uname_check(
|
||
|
cmd_parms *cmd,
|
||
|
void *cfg,
|
||
|
int arg)
|
||
|
{
|
||
|
authn_casa_dir_cfg *pDirConfig = (authn_casa_dir_cfg*) cfg;
|
||
|
|
||
|
// Record the value in our structure
|
||
|
pDirConfig->performUsernameCheck = arg;
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Configuration directives array structure.
|
||
|
//
|
||
|
static const command_rec authn_casa_cmds[] =
|
||
|
{
|
||
|
AP_INIT_FLAG("UsernameCheck", // tbd - May be this directive should be on a per-directory or per-location basis
|
||
|
set_authn_casa_uname_check,
|
||
|
NULL,
|
||
|
OR_AUTHCFG,
|
||
|
"Check for username == CasaPrincipal (Value limited to 'on' or 'off')"),
|
||
|
{NULL}
|
||
|
};
|
||
|
|
||
|
/* ************************************************************************
|
||
|
* check_password()
|
||
|
*
|
||
|
* Given a user and password, expected to return AUTH_GRANTED if we
|
||
|
* can validate the user/password combination.
|
||
|
*
|
||
|
* L2
|
||
|
* ************************************************************************/
|
||
|
static authn_status
|
||
|
check_password(
|
||
|
request_rec *r,
|
||
|
const char *user,
|
||
|
const char *password)
|
||
|
{
|
||
|
authn_status retStatus;
|
||
|
authn_casa_dir_cfg *pDirConfig;
|
||
|
bool userNameChecked = false;
|
||
|
int i;
|
||
|
char *pLocationName;
|
||
|
|
||
|
// First determine the length of the name of the location being protected
|
||
|
i = 0;
|
||
|
while (r->uri[i] != '\0')
|
||
|
{
|
||
|
if (r->uri[i] == '/')
|
||
|
{
|
||
|
// Ignore the slash if it is at the beginning of the uri
|
||
|
if (i != 0)
|
||
|
{
|
||
|
// The slash is not at the beggining of the uri, stop.
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
// Now get a copy of the location being protected
|
||
|
if (i > 1)
|
||
|
{
|
||
|
pLocationName = apr_palloc(r->pool, i);
|
||
|
if (pLocationName)
|
||
|
{
|
||
|
memset(pLocationName, 0, i);
|
||
|
memcpy(pLocationName, &(r->uri[1]), i - 1); // Do not include the slashes
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, 0, r, "Memory allocation failure");
|
||
|
return AUTH_GENERAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// We are protecting the server root
|
||
|
pLocationName = "apache_root";
|
||
|
}
|
||
|
|
||
|
// Get access to our per-dir configuration structure
|
||
|
pDirConfig = ap_get_module_config(r->per_dir_config,
|
||
|
&authn_casa_module);
|
||
|
if (pDirConfig)
|
||
|
{
|
||
|
// Assume success
|
||
|
retStatus = AUTH_GRANTED;
|
||
|
|
||
|
// Check if we must perform the username check
|
||
|
if (pDirConfig->performUsernameCheck != 0)
|
||
|
{
|
||
|
// Remember that we performed this check
|
||
|
userNameChecked = true;
|
||
|
|
||
|
// Check if the username matches the name what we are expecting
|
||
|
if (strcmp(user, "CasaPrincipal") != 0)
|
||
|
{
|
||
|
// The username does not match, allow other providers to get
|
||
|
// a crack to it.
|
||
|
retStatus = AUTH_USER_NOT_FOUND;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check the token if a problem has not been found
|
||
|
if (retStatus == AUTH_GRANTED)
|
||
|
{
|
||
|
CasaStatus casaStatus;
|
||
|
PrincipalIf *pPrincipalIf;
|
||
|
|
||
|
// Validate the token
|
||
|
casaStatus = ValidateAuthToken(pLocationName,
|
||
|
password,
|
||
|
strlen(password),
|
||
|
&pPrincipalIf);
|
||
|
if (CASA_SUCCESS(casaStatus))
|
||
|
{
|
||
|
|
||
|
int buffLen = 0;
|
||
|
apr_table_t *e = r->subprocess_env;
|
||
|
|
||
|
// Associate necessary environment variables with the request block
|
||
|
casaStatus = pPrincipalIf->getIdentityId(pPrincipalIf,
|
||
|
NULL,
|
||
|
&buffLen);
|
||
|
if (CasaStatusCode(casaStatus) == CASA_STATUS_BUFFER_OVERFLOW)
|
||
|
{
|
||
|
char *pBuff;
|
||
|
|
||
|
// Allocate buffer to obtain the Identity Id
|
||
|
pBuff = apr_pcalloc(r->pool, buffLen);
|
||
|
if (pBuff)
|
||
|
{
|
||
|
// Read the value into our buffer
|
||
|
if (CASA_SUCCESS(pPrincipalIf->getIdentityId(pPrincipalIf,
|
||
|
pBuff,
|
||
|
&buffLen)))
|
||
|
{
|
||
|
// Now set the environment variable
|
||
|
apr_table_setn(e, CasaIdentityIdEnvVar, pBuff);
|
||
|
|
||
|
// Also, update the username within the request block with the identity id
|
||
|
r->user = pBuff;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, 0, r, "Unable to obtain identity id");
|
||
|
retStatus = AUTH_GENERAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, 0, r, "Memory allocation failure");
|
||
|
retStatus = AUTH_GENERAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, 0, r, "Un-expected error obtaining identity id, %08X", casaStatus);
|
||
|
retStatus = AUTH_GENERAL_ERROR;
|
||
|
}
|
||
|
|
||
|
if (retStatus == AUTH_GRANTED)
|
||
|
{
|
||
|
buffLen = 0;
|
||
|
casaStatus = pPrincipalIf->getSourceName(pPrincipalIf,
|
||
|
NULL,
|
||
|
&buffLen);
|
||
|
if (CasaStatusCode(casaStatus) == CASA_STATUS_BUFFER_OVERFLOW)
|
||
|
{
|
||
|
char *pBuff;
|
||
|
|
||
|
// Allocate buffer to obtain the Identity Source Name
|
||
|
pBuff = apr_pcalloc(r->pool, buffLen);
|
||
|
if (pBuff)
|
||
|
{
|
||
|
// Read the value into our buffer
|
||
|
if (CASA_SUCCESS(pPrincipalIf->getSourceName(pPrincipalIf,
|
||
|
pBuff,
|
||
|
&buffLen)))
|
||
|
{
|
||
|
// Now set the environment variable
|
||
|
apr_table_setn(e, CasaIdentitySourceNameEnvVar, pBuff);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, 0, r, "Unable to obtain identity source name");
|
||
|
retStatus = AUTH_GENERAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, 0, r, "Memory allocation failure");
|
||
|
retStatus = AUTH_GENERAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, 0, r, "Un-expected error obtaining identity source name, %08X", casaStatus);
|
||
|
retStatus = AUTH_GENERAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (retStatus == AUTH_GRANTED)
|
||
|
{
|
||
|
buffLen = 0;
|
||
|
casaStatus = pPrincipalIf->getSourceUrl(pPrincipalIf,
|
||
|
NULL,
|
||
|
&buffLen);
|
||
|
if (CasaStatusCode(casaStatus) == CASA_STATUS_BUFFER_OVERFLOW)
|
||
|
{
|
||
|
char *pBuff;
|
||
|
|
||
|
// Allocate buffer to obtain the Identity Source Url
|
||
|
pBuff = apr_pcalloc(r->pool, buffLen);
|
||
|
if (pBuff)
|
||
|
{
|
||
|
// Read the value into our buffer
|
||
|
if (CASA_SUCCESS(pPrincipalIf->getSourceUrl(pPrincipalIf,
|
||
|
pBuff,
|
||
|
&buffLen)))
|
||
|
{
|
||
|
// Now set the environment variable
|
||
|
apr_table_setn(e, CasaIdentitySourceUrlEnvVar, pBuff);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, 0, r, "Unable to obtain identity source url");
|
||
|
retStatus = AUTH_GENERAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, 0, r, "Memory allocation failure");
|
||
|
retStatus = AUTH_GENERAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, 0, r, "Un-expected error obtaining identity source url, %08X", casaStatus);
|
||
|
retStatus = AUTH_GENERAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (retStatus == AUTH_GRANTED)
|
||
|
{
|
||
|
char *pAttribNameBuff, *pAttribValueBuff;
|
||
|
int enumHandle = 0;
|
||
|
int attribNameBuffLen, attribValueBuffLen;
|
||
|
|
||
|
while (retStatus == AUTH_GRANTED)
|
||
|
{
|
||
|
// Get attribute lengths
|
||
|
attribNameBuffLen = attribValueBuffLen = 0;
|
||
|
casaStatus = pPrincipalIf->attributeEnumerate(pPrincipalIf,
|
||
|
&enumHandle,
|
||
|
NULL,
|
||
|
&attribNameBuffLen,
|
||
|
NULL,
|
||
|
&attribValueBuffLen);
|
||
|
if (CasaStatusCode(casaStatus) == CASA_STATUS_BUFFER_OVERFLOW)
|
||
|
{
|
||
|
// Allocate buffers to obtain the attribute data
|
||
|
pAttribNameBuff = apr_pcalloc(r->pool, attribNameBuffLen);
|
||
|
pAttribValueBuff = apr_pcalloc(r->pool, attribValueBuffLen);
|
||
|
if (pAttribNameBuff && pAttribValueBuff)
|
||
|
{
|
||
|
// Read the attribute into our buffer
|
||
|
if (CASA_SUCCESS(pPrincipalIf->attributeEnumerate(pPrincipalIf,
|
||
|
&enumHandle,
|
||
|
pAttribNameBuff,
|
||
|
&attribNameBuffLen,
|
||
|
pAttribValueBuff,
|
||
|
&attribValueBuffLen)))
|
||
|
{
|
||
|
// Now set the environment variable
|
||
|
apr_table_setn(e, pAttribNameBuff, pAttribValueBuff);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, 0, r, "Unable to obtain identity attribute");
|
||
|
retStatus = AUTH_GENERAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, 0, r, "Memory allocation failure");
|
||
|
retStatus = AUTH_GENERAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Check if we are done going through the attributes
|
||
|
if (CasaStatusCode(casaStatus) == CASA_STATUS_NO_MORE_ENTRIES)
|
||
|
{
|
||
|
// Done
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, 0, r, "Un-expected error during attribute enumeration, %08X", casaStatus);
|
||
|
retStatus = AUTH_GENERAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Release the principal interface instance
|
||
|
pPrincipalIf->releaseReference(pPrincipalIf);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Check if the token validation failed for a CasaPrincipal
|
||
|
if (userNameChecked)
|
||
|
{
|
||
|
// Token validation failed for a CasaPrincipal, always return AUTH_DENIED.
|
||
|
retStatus = AUTH_DENIED;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// We did not check the username, set the return status based on the status
|
||
|
// returned by ValidateAuthToken().
|
||
|
if (CasaStatusCode(casaStatus) == CASA_STATUS_AUTHENTICATION_FAILURE)
|
||
|
{
|
||
|
// Authentication failed
|
||
|
retStatus = AUTH_DENIED;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Allow other providers to get a crack to it
|
||
|
retStatus = AUTH_USER_NOT_FOUND;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ap_log_rerror(APLOG_MARK, APLOG_ALERT, 0, r, "Did not get module per-server config structure");
|
||
|
retStatus = AUTH_GENERAL_ERROR;
|
||
|
}
|
||
|
|
||
|
return retStatus;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Authentication Provider Function Table
|
||
|
//
|
||
|
static const authn_provider authn_casa_provider =
|
||
|
{
|
||
|
&check_password,
|
||
|
NULL, // We do not support Digest Authentication
|
||
|
};
|
||
|
|
||
|
/* ************************************************************************
|
||
|
* register_hooks()
|
||
|
*
|
||
|
* Register all of the module hooks.
|
||
|
*
|
||
|
* L2
|
||
|
* ************************************************************************/
|
||
|
static void
|
||
|
register_hooks(
|
||
|
apr_pool_t *p)
|
||
|
{
|
||
|
// Register as an authentication provider
|
||
|
ap_register_provider(p,
|
||
|
AUTHN_PROVIDER_GROUP, // Provider group
|
||
|
"casa", // Provider name
|
||
|
"0", // Provider version
|
||
|
&authn_casa_provider); // Authentication Provider function table
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Declare ourselves to the HTTPD core.
|
||
|
//
|
||
|
module AP_MODULE_DECLARE_DATA authn_casa_module =
|
||
|
{
|
||
|
STANDARD20_MODULE_STUFF,
|
||
|
create_per_dir_config, // Create per-dir config structures
|
||
|
NULL, // merge per-dir config structures
|
||
|
NULL, // Create per-server config structures
|
||
|
NULL, // merge per-server config structures
|
||
|
authn_casa_cmds, // command handlers
|
||
|
register_hooks // register hooks
|
||
|
};
|