/*********************************************************************** * * 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 * ***********************************************************************/ #include #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 + 1); if (pLocationName) { memset(pLocationName, 0, i + 1); 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, %0X", 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, %0X", 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, %0X", 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, %0X", 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, 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 };