/*
* Copyright [2008] [Jens Frey]
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
/*
* If you have any questions feel free to contact me
* at jens.frey@coffeecrew.org
*
*/
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"
#include "http_log.h"
#include "mod_auth.h"
#include "libykclient.h"
#include "ap_provider.h"
#include "apr_strings.h"
#include "apr_dbm.h"
#include "apr_time.h"
#include "apr_md5.h"
#include "http_core.h"
#include "http_request.h"
#define APR_WANT_STRFUNC /* for strcasecmp */
#include "apr_want.h"
#define YUBIKEY_TOKEN_LENGTH 44
#define YUBIKEY_ID_LENGTH 12
#define LOG_PREFIX "[mod_authn_yubikey] "
#define MOD_AUTHN_YUBIKEY_NAME "mod_authn_yubikey"
#define MOD_AUTHN_YUBIKEY_VERSION "0.1"
#define ERROR_TEXT "
" \
"Error: SSL (https) connection required" \
"" \
"" \
"
" \
"
This site is configured to require an SSL (https) connection.
" \
"
" \
"" \
"You may want to try chaning http to https in your address bar.
" \
"If you think this is an error, please contact the administrator." \
"
"
#define HDR_YK_AUTH_TYPE "X-Yubi-Auth-Type"
/* One factor (just token) */
#define YK_AUTH_TYPE_OF "OneFactor"
/* Two factor , password and token */
#define YK_AUTH_TYPE_TF "TwoFactor"
module AP_MODULE_DECLARE_DATA authn_yubikey_module;
/* Default values */
#define DEFAULT_TIMEOUT 43200 /* 12h */
#define DEFAULT_REQUIRE_SECURE (TRUE)
#define DEFAULT_EXTERNAL_ERROR_PAGE (FALSE)
#define DEFAULT_USER_DB "conf/ykUserDb"
#define DEFAULT_TMP_DB "conf/ykTmpDb"
#define UNSET -1
typedef struct
{
/* This is the actual timeout after which the session finally expires,
* there is NO recovery from this, so this timeout is not renewed everytime
* a user makes a request
*/
int timeoutSeconds;
/* This flag requires the protected location to be accessed via an secure Url
* this is especially useful if you use the two factor authentication,
* since passwords would otherwise be sent in the clear.
*/
int requireSecure;
/* If any error happens, this will redirect you to the given error page,
* or an internally generated error page will be shown.
* Use this is you want to customize the error page.
*/
int externalErrorPage;
/* This is the temporary filename authenticated user are saved in.
* This could possibly be done with an in memory version of s.th. similar
* to the database
*/
const char *tmpAuthDbFilename;
/* This is the file where the actual user/password connection happens. So
* the module knows where it can find the file where the tokenId/username
* mapping happens.
*/
const char *userAuthDbFilename;
/* TODO: NYI
* This is required to be given if you want to use another authentication
* provider which supports the yubikey token, but not via yubicos site.
*/
//const char *validationUrl;
} yubiauth_dir_cfg;
/* A helper */
static apr_datum_t string2datum(const char * toStore, request_rec *r)
{
apr_datum_t dt;
dt.dptr = apr_pstrdup(r->pool, (char*) toStore);
#ifndef NETSCAPE_DBM_COMPAT
dt.dsize = strlen(dt.dptr);
#else
dt.dsize = strlen(dt.dptr) + 1;
#endif
return dt;
}
static void openDb(apr_dbm_t **userDbm, const char *dbFilename, request_rec *r)
{
apr_status_t rv;
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r, LOG_PREFIX "Opening db ...");
rv = apr_dbm_open(userDbm, dbFilename, APR_DBM_RWCREATE, APR_FPROT_OS_DEFAULT, r->pool);
if (rv != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX "Error opening db %s ...", dbFilename);
}
}
static void closeDb(apr_dbm_t *userDbm, request_rec *r)
{
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r, LOG_PREFIX "Closing db ...");
apr_dbm_close(userDbm);
}
/* User Key because the username is the key to the db */
static void deleteKeyFromDb(apr_dbm_t *userDbm, const char *userKey, request_rec *r)
{
apr_datum_t key;
apr_status_t rv;
key = string2datum(userKey, r);
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r,
LOG_PREFIX "Deleting key %s",
key.dptr);
rv = apr_dbm_delete(userDbm, key);
if (rv != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
LOG_PREFIX "Could not delete key %s",
key.dptr);
}
}
static apr_status_t setUserInDb(apr_dbm_t *userDbm,
const char *user,
const char *password,
request_rec *r)
{
char *timeAuthenticated = NULL;
char *dbToken = NULL; //This is used to store pw:date
apr_datum_t key, value;
apr_status_t rv;
/* Built up some combination of token:time */
timeAuthenticated = apr_psprintf(r->pool, "%" APR_TIME_T_FMT, (apr_time_t) (apr_time_sec(apr_time_now())));
dbToken = apr_pstrcat(r->pool, password, ":", timeAuthenticated, NULL);
/* store OTP:time combo with username as key in DB */
key = string2datum(user, r);
value = string2datum(dbToken, r);
/* Pump user into db, store un, cookie value, creation date,
* let this expire sometime
*/
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r, LOG_PREFIX "Writing key: %s and value: %s to db",
key.dptr, value.dptr);
rv = apr_dbm_store(userDbm, key, value);
if (rv != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX "Error writing to db ... with key: %s and value: %s",
key.dptr, value.dptr);
}
/* Spit back, so we can decide wheather s.th. went wrong or not */
return rv;
}
static int passwordExpired(const char *user,
apr_time_t lookedUpDate,
apr_time_t timeout,
request_rec *r)
{
if ((apr_time_sec(apr_time_now())) > (lookedUpDate + timeout)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r, LOG_PREFIX "Session expired for user %s", user);
return TRUE;
}
return FALSE;
}
static int isUserValid(const char *user,
const char *password,
yubiauth_dir_cfg *cfg,
request_rec *r)
{
ap_configfile_t *f;
char l[MAX_STRING_LEN];
apr_status_t status;
char *file_password = NULL;
char *yubiKeyId = NULL;
char *userPassword = NULL;
apr_size_t passwordLength = 0;
char *realName = NULL;
int userWasFound = FALSE;
/* This is TRUE when we store a combination of yubikeyId:username:password,
* we then have a two factor authentication.
*/
int tokenHasPassword = FALSE;
status = ap_pcfg_openfile(&f, r->pool, cfg->userAuthDbFilename);
if (status != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
LOG_PREFIX "Could not open AuthYkUserFile file: %s",
cfg->userAuthDbFilename);
return FALSE;
}
/* Do length check of at least the password part,
* to be a yubikey token, it has to have at least 44
* characters from where the first 12 are the ID of the user.
*/
if (strlen(password) < YUBIKEY_TOKEN_LENGTH) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
LOG_PREFIX "The entered password cannot be a yubikey generated token");
ap_cfg_closefile(f);
return FALSE;
}
/* If the password is bigger then 44 characters, then we have an additional password
* set into the field, since the produced token by the yubikey is 44 characters long
*/
passwordLength = (apr_size_t) strlen(password) - YUBIKEY_TOKEN_LENGTH;
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r,
LOG_PREFIX "The length of the entered password is: %d", passwordLength);
/* We have to distinct between a 44 character string which is the
* toke output only and a longer string, which would contain a
* password at its beginning
*/
if (strlen(password) > YUBIKEY_TOKEN_LENGTH) {
/* copy off the password part from the password string */
userPassword = apr_pstrndup(r->pool, password, passwordLength);
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r,
LOG_PREFIX "The entered password is: %s", userPassword);
}
/* Now move the password pointer forward the number of calculatd characters for the userPassword,
* we move the pointer beyond the last read character (not -1), to start reading the real stuff
*/
yubiKeyId = apr_pstrndup(r->pool, &password[passwordLength], (apr_size_t) YUBIKEY_ID_LENGTH);
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r,
LOG_PREFIX "The calculated YubiKey ID is: %s", yubiKeyId);
/* Find the TokenID/UN:PW solution in the file */
while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) {
const char *rpw, *w;
char *unPw = NULL;
/* Skip # or blank lines. */
if ((l[0] == '#') || (!l[0])) {
continue;
}
rpw = l;
w = ap_getword(r->pool, &rpw, ':');
/* The first 12 chars are the ID which must be available in this file
* else the user might be a yubikey user, but possibly a user we don't
* want.
*/
if (!strncmp(yubiKeyId, w, 12)) {
/* This would fetch the real username,
* after the ID could be located
*/
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r,
LOG_PREFIX "Could find the ID: %s", w);
/* remember, since we are working with the passwd
* utility, this realName is hashed
*/
realName = ap_getword(r->pool, &rpw, '\n');
apr_table_set(r->headers_in, HDR_YK_AUTH_TYPE, YK_AUTH_TYPE_OF);
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r,
LOG_PREFIX "The looked up realname is: %s", realName);
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r,
LOG_PREFIX "The looked up userPassword is: %s", userPassword);
/* this results in username:password as it should be entered in the install dialog */
if (userPassword) {
unPw = apr_pstrcat(r->pool, user, ":", userPassword, NULL);
apr_table_set(r->headers_in, HDR_YK_AUTH_TYPE, YK_AUTH_TYPE_TF);
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r,
LOG_PREFIX "The built un:pw combo is: %s", unPw);
}
/* If there is a password set, use the username:password combo,
* else just compare the username
*/
status = apr_password_validate(userPassword?unPw:user, realName);
if (status == APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r,
LOG_PREFIX "Could map ID %s to User: %s", w, user);
userWasFound = TRUE;
break;
}
}
}
ap_cfg_closefile(f);
return userWasFound;
}
/* This does some initial checking, like if we're running on a SSL line or not */
static int checkInitial(request_rec *r)
{
yubiauth_dir_cfg *cfg = ap_get_module_config(r->per_dir_config, &authn_yubikey_module);
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r,
LOG_PREFIX "requireSecure: %d", cfg->requireSecure);
/* If no securiy is wanted or scheme is already https */
if (!cfg->requireSecure || !strncmp(ap_http_scheme(r), "https", 5)) {
return OK;
}
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
LOG_PREFIX "The server is configured to use HTTPS on URI: %s", r->uri);
if(cfg->externalErrorPage == TRUE){
/* We explicitly want that to be overridable */
//return HTTP_BAD_REQUEST;
return HTTP_NOT_ACCEPTABLE;
}
/* Tell the user/admin what's going on instead of just showing BAD_REQUEST */
ap_rputs(DOCTYPE_HTML_4_0T, r);
ap_set_content_type(r, "text/html;");
ap_rputs(ERROR_TEXT, r);
ap_finalize_request_protocol(r);
return HTTP_BAD_REQUEST;
}
static authn_status authn_check_otp(request_rec *r, const char *user,
const char *password)
{
apr_status_t rv;
apr_dbm_t *userDbm = NULL;
yubiauth_dir_cfg *cfg = ap_get_module_config(r->per_dir_config, &authn_yubikey_module);
apr_datum_t key,dbUserRecord;
key.dptr = NULL;
dbUserRecord.dptr = NULL;
char *lookedUpToken = NULL;
char *lookedUpPassword = NULL; //This is the OTP token
apr_size_t passwordLength = 0;
apr_time_t lookedUpDate = 0;
/* No username and no password is set */
if (!*user || !*password)
return AUTH_DENIED;
/* Since the password field contains possibly a password and the OTP token, we
* have to break that up here
*/
passwordLength = (apr_size_t) strlen(password) - YUBIKEY_TOKEN_LENGTH;
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r,
LOG_PREFIX "Username is: %s and password is: %s", user, &password[passwordLength]);
/* Now open the User DB and see if the user really is one of us.
* for that we save the 12char token:username combo.
* Ideally we can fill that with the htpasswd utility
* NOTE: enter full password here
*/
if (!isUserValid(user, password, cfg, r)) {
return AUTH_DENIED;
}
openDb(&userDbm, cfg->tmpAuthDbFilename, r);
key = string2datum(user, r);
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r, LOG_PREFIX "Fetching token (pw:time) for user %s from db ...", user);
rv = apr_dbm_fetch(userDbm, key, &dbUserRecord);
if (rv != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX "unable to fetch the user (%s) from the"
"Database, better abort here.", user);
closeDb(userDbm, r);
return HTTP_INTERNAL_SERVER_ERROR;
}
if (dbUserRecord.dptr != NULL) {
/* it's separated pw:time here */
const char *sep = ":";
char *time;
lookedUpToken = apr_pstrmemdup(r->pool, dbUserRecord.dptr, dbUserRecord.dsize);
/* Break down the token into it's pw:time components */
lookedUpPassword = apr_strtok(lookedUpToken, sep, &time);
lookedUpDate = (apr_time_t) apr_atoi64(time);
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r,
LOG_PREFIX "We could extrace these values from the token:");
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r,
LOG_PREFIX "The looked up token for the user: %s",
user);
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r,
LOG_PREFIX "The looked up password: %s",
lookedUpPassword);
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r,
LOG_PREFIX "The looked up time: %" APR_TIME_T_FMT,
lookedUpDate);
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r,
LOG_PREFIX "The looked up token: %s",
lookedUpToken);
}
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_DEBUG, 0, r, LOG_PREFIX "Fetched token (%s) ...", lookedUpToken);
/* password has to be set, if the pw content is NULL or empty, we have
* catched that earlier ...
*/
if (lookedUpPassword != NULL && !strcmp(lookedUpPassword, &password[passwordLength])) {
/* The date expired */
if (passwordExpired(user, lookedUpDate, cfg->timeoutSeconds, r)) {
/* Delete user record */
deleteKeyFromDb(userDbm, user, r);
closeDb(userDbm, r);
return AUTH_DENIED;
}
else {
closeDb(userDbm, r);
return AUTH_GRANTED;
}
}
else {
int authenticationSuccessful = 0;
int ret = YUBIKEY_CLIENT_BAD_OTP;
/* We could not lookup the password, verify the sent password */
ret = yubikey_client_simple_request(&password[passwordLength], 1, 0, NULL);
if (ret == YUBIKEY_CLIENT_OK) {
authenticationSuccessful = 1;
} else {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
LOG_PREFIX "Authentication failed, reason: %s",
yubikey_client_strerror(ret));
return AUTH_DENIED;
}
/* We could successfully authenticate the user */
if (authenticationSuccessful) {
/* Try to write the user into the db */
if (setUserInDb(userDbm, user, &password[passwordLength], r)
!= APR_SUCCESS) {
/* Abort, we could not write the user into the db after
* authenticating him ...
*/
closeDb(userDbm, r);
return HTTP_INTERNAL_SERVER_ERROR;
}
/* User could be written to the db*/
closeDb(userDbm, r);
return AUTH_GRANTED;
}
/* Could not authenticate successful */
closeDb(userDbm, r);
return AUTH_DENIED;
}
/* Something went wrong or we did not think about it, better deny */
closeDb(userDbm, r);
return AUTH_DENIED;
}
/* This provides a simple UI for accessing the key database
* used to grant access to users owning a yubikey or not.
*
* This is primarily a web version of htaccess.
*/
static int authn_yubikey_handler(request_rec *r)
{
ap_configfile_t *f;
char l[MAX_STRING_LEN];
apr_status_t status;
char *file_password = NULL;
char *yubiKeyId = NULL;
char *realName = NULL;
apr_file_t *dbFormFile = NULL;
yubiauth_dir_cfg *cfg = ap_get_module_config(r->per_dir_config, &authn_yubikey_module);
if (strcmp(r->handler, "authn_yubikey")) {
return DECLINED;
}
r->content_type = "text/html";
/* Post back */
if (r->method_number == M_POST) {
const char *postbackContent = NULL;
char *tmp = NULL;
char buffer[1024];
//Read the POST data sent from the client
ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
if ( ap_should_client_block(r) == 1 ) {
while ( ap_get_client_block(r, buffer, 1024) > 0 ) {
postbackContent = apr_pstrcat(r->pool, buffer, tmp, NULL);
//tmp = apr_pstrdup(r->pool, postbackContent);
}
}
else {
ap_rputs("No POST data available",r);
}
//We have the data now, now process it and save it into the db
ap_set_content_type(r, "text/plain;");
ap_rprintf(r, "Postback content: %s", postbackContent);
return OK;
}
/* Serve content if it's a GET request */
if (!r->header_only) {
ap_rputs("YubiAuth user management", r);
ap_rputs("Welcome to the YubiAuth user Mgmt.
", r);
ap_rputs("The following users could be found inside the database:
", r);
//Open userDb file for looped output
status = ap_pcfg_openfile(&f, r->pool, cfg->userAuthDbFilename);
if (status != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
LOG_PREFIX "Could not open YubiAuthUserDb file: %s",
cfg->userAuthDbFilename);
return HTTP_INTERNAL_SERVER_ERROR;
}
ap_rputs("TokenId | Real Name |
\n", r);
while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) {
const char *rpw, *w;
/* Skip # or blank lines. */
if ((l[0] == '#') || (!l[0])) {
continue;
}
rpw = l;
w = ap_getword(r->pool, &rpw, ':');
//yubiKeyId = apr_pstrndup(r->pool, password, (apr_size_t) 12);
//realName = ap_getword(r->pool, &rpw, ':');
ap_rputs("", r);
ap_rprintf(r, "%s | ", w);
ap_rprintf(r, "%s | \n", rpw);
ap_rputs("
", r);
}
ap_cfg_closefile(f);
ap_rputs("
", r);
ap_rputs("Want to add a user?
.", r);
ap_rputs("", r);
ap_rputs("", r);
}
//If we receive a POST to this location, we probably want to update the userdb
//be sure to check if there is a user set and that this user is allowed to change information
// inside the userdb, we cn authenticate the user with the OTP token here too.
//We should make the administrative user configurable, by specifying the token which is allowed
// access ...
return OK;
}
static const authn_provider authn_yubikey_provider = {
&authn_check_otp,
NULL
};
static int init_mod_yk(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
{
ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
LOG_PREFIX "Version [" MOD_AUTHN_YUBIKEY_VERSION "] initialized");
ap_add_version_component(pconf, MOD_AUTHN_YUBIKEY_NAME "/" MOD_AUTHN_YUBIKEY_VERSION);
return OK;
}
static void authn_yubikey_register_hooks(apr_pool_t *p)
{
//static const char *const aszSucc[] = { "mod_authz_user.c", NULL };
//ap_hook_auth_checker(authz_check_yubi_user, NULL, aszSucc, APR_HOOK_MIDDLE);
/* No content handler for this one */
//ap_hook_handler(authn_yubikey_handler, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_header_parser(checkInitial, NULL, NULL, APR_HOOK_MIDDLE);
ap_register_provider(p, AUTHN_PROVIDER_GROUP, "yubikey", "0", &authn_yubikey_provider);
ap_hook_post_config(init_mod_yk, NULL, NULL, APR_HOOK_MIDDLE);
}
static const command_rec authn_yubikey_cmds[] = {
AP_INIT_TAKE1("AuthYubiKeyTmpFile", ap_set_file_slot,
(void*) APR_OFFSETOF(yubiauth_dir_cfg, tmpAuthDbFilename),
ACCESS_CONF, "The temporary filename for authenticated users"),
AP_INIT_TAKE1("AuthYubiKeyUserFile", ap_set_file_slot,
(void*) APR_OFFSETOF(yubiauth_dir_cfg, userAuthDbFilename),
ACCESS_CONF, "The filename for allowed users"),
AP_INIT_TAKE1("AuthYubiKeyTimeout", ap_set_int_slot,
(void*) APR_OFFSETOF(yubiauth_dir_cfg, timeoutSeconds),
ACCESS_CONF, "The timeout when users have to reauthenticate (Default 43200 seconds [12h])"),
//AP_INIT_TAKE1("AuthYkValidationUrl", ap_set_int_slot,
// (void*) APR_OFFSETOF(yubiauth_dir_cfg, validationUrl),
// ACCESS_CONF, "The URL of the location where the key can be" \
// "authenticated"),
AP_INIT_FLAG("AuthYubiKeyExternalErrorPage", ap_set_flag_slot,
(void*) APR_OFFSETOF(yubiauth_dir_cfg, externalErrorPage),
ACCESS_CONF, "If SSL is required display internal error page, or display custom (406) error" \
"page (Default Off)"),
AP_INIT_FLAG("AuthYubiKeyRequireSecure", ap_set_flag_slot,
(void*) APR_OFFSETOF(yubiauth_dir_cfg, requireSecure),
ACCESS_CONF|RSRC_CONF,
"Whether or not a secure site is required to pass authentication (Default On)"),
{NULL}
};
static void *create_yubiauth_dir_cfg(apr_pool_t *pool, char *x)
{
yubiauth_dir_cfg *dir = apr_pcalloc(pool, sizeof (yubiauth_dir_cfg));
/* Set defaults configuration here
*/
dir->timeoutSeconds = UNSET;
dir->requireSecure = UNSET;
dir->externalErrorPage = UNSET;
dir->tmpAuthDbFilename = NULL;
dir->userAuthDbFilename = NULL;
return dir;
}
static void *merge_yubiauth_dir_cfg(apr_pool_t *pool, void *BASE, void *ADD)
{
yubiauth_dir_cfg *base = BASE;
yubiauth_dir_cfg *add = ADD;
yubiauth_dir_cfg *dir = apr_pcalloc(pool, sizeof (yubiauth_dir_cfg));
/* merge */
dir->timeoutSeconds = (add->timeoutSeconds == UNSET) ? base->timeoutSeconds : add->timeoutSeconds;
dir->requireSecure = (add->requireSecure == UNSET) ? base->requireSecure : add->requireSecure;
dir->externalErrorPage = (add->externalErrorPage == UNSET) ? base->externalErrorPage : add->externalErrorPage;
dir->userAuthDbFilename = (add->userAuthDbFilename == NULL) ? base->userAuthDbFilename : add->userAuthDbFilename;
dir->tmpAuthDbFilename = (add->tmpAuthDbFilename == NULL) ? base->tmpAuthDbFilename : add->tmpAuthDbFilename;
/* Set defaults configuration here
*/
if (dir->timeoutSeconds == UNSET) {
dir->timeoutSeconds = DEFAULT_TIMEOUT;
}
if (dir->requireSecure == UNSET) {
dir->requireSecure = DEFAULT_REQUIRE_SECURE;
}
if (dir->externalErrorPage == UNSET) {
dir->externalErrorPage = DEFAULT_EXTERNAL_ERROR_PAGE;
}
if (dir->userAuthDbFilename == NULL) {
dir->userAuthDbFilename = ap_server_root_relative(pool, DEFAULT_USER_DB);
}
if (dir->tmpAuthDbFilename == NULL) {
dir->tmpAuthDbFilename = ap_server_root_relative(pool, DEFAULT_TMP_DB);
}
return dir;
}
/* Dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA authn_yubikey_module = {
STANDARD20_MODULE_STUFF,
&create_yubiauth_dir_cfg, /* create per-dir config structures */
&merge_yubiauth_dir_cfg, /* merge per-dir config structures */
NULL, /* create per-server config structures */
NULL, /* merge per-server config structures */
authn_yubikey_cmds, /* table of config file commands */
authn_yubikey_register_hooks /* register hooks */
};