summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--module/consumer.cc24
-rw-r--r--module/mod_auth_singleid.c273
-rw-r--r--module/mod_auth_singleid.h3
3 files changed, 215 insertions, 85 deletions
diff --git a/module/consumer.cc b/module/consumer.cc
index 32db8cb..9c5da89 100644
--- a/module/consumer.cc
+++ b/module/consumer.cc
@@ -16,6 +16,9 @@ using opkele::failed_discovery;
using opkele::failed_lookup;
using opkele::failed_xri_resolution;
using opkele::id_res_bad_nonce;
+using opkele::id_res_bad_return_to;
+using opkele::id_res_failed;
+using opkele::id_res_mismatch;
using opkele::no_endpoint;
using opkele::openid_endpoint_t;
using opkele::openid_message_t;
@@ -271,8 +274,13 @@ Consumer::retrieve_assoc(const string& server, const string& handle)
}
}
+ /*
+ * Yes, we return this when not found, it helps the user experience, if
+ * apache restarted.
+ */
+
if (!assoc)
- throw failed_lookup("could not retrieve association for server: " + server);
+ throw dumb_RP("could not retrieve association for server: " + server);
return assoc_t(assoc);
}
@@ -362,7 +370,7 @@ process_ax_values (sid_request_t *req, sid_attribute_t *attr, const string_list&
array[i] = it->c_str();
array[i] = NULL;
- sid_request_attribute_values (req, attr, array);
+ sid_request_attribute_values (req, attr, array, values.size());
delete [] array;
}
@@ -483,6 +491,18 @@ complete_auth (sid_request_t *req, Consumer &consumer, params_t &params,
string identity = consumer.get_claimed_id();
sid_request_authenticated (req, identity.c_str());
parse_ax_attributes(req, params, attributes);
+ } catch (id_res_mismatch &ex) {
+ sid_request_respond (req, 403, "Signature mismatch", NULL);
+ sid_request_log_error (req, "signature did not match data", ex.what());
+ } catch (bad_input &ex) {
+ sid_request_respond (req, 403, "Bad authentication input", NULL);
+ sid_request_log_error (req, "bad input", ex.what());
+ } catch (id_res_bad_return_to &ex) {
+ sid_request_respond (req, 403, "Bad authenticated address", NULL);
+ sid_request_log_error (req, "bad return to", ex.what());
+ } catch (id_res_failed &ex) {
+ sid_request_respond (req, 503, "Service error, try again", NULL);
+ sid_request_log_error (req, "checking response failed", ex.what());
} catch (exception &ex) {
sid_request_respond (req, 500, NULL, NULL);
sid_request_log_error (req, "error while completing authentication", ex.what());
diff --git a/module/mod_auth_singleid.c b/module/mod_auth_singleid.c
index e9d195a..41402ed 100644
--- a/module/mod_auth_singleid.c
+++ b/module/mod_auth_singleid.c
@@ -82,6 +82,16 @@ enum {
};
/*
+ * Per request information.
+ */
+
+typedef struct sid_session {
+ char *identifier;
+ apr_array_header_t *values;
+ time_t expiry;
+} sid_session_t;
+
+/*
* Per directory configuration.
*/
typedef struct sid_context {
@@ -114,6 +124,8 @@ get_token (apr_pool_t *pool, const char **line, const char *delims)
{
const char *beg, *end;
const char *str = *line;
+ char *result, *from, *at;
+ int quoted = 0;
/* Find first non-white byte */
while (*str && apr_isspace(*str))
@@ -136,6 +148,7 @@ get_token (apr_pool_t *pool, const char **line, const char *delims)
/* Trim the quotes if present */
if (beg + 1 < end && *beg == '"' && *(end - 1) == '"') {
+ quoted = 1;
++beg;
--end;
}
@@ -149,7 +162,23 @@ get_token (apr_pool_t *pool, const char **line, const char *delims)
++str;
*line = str;
- return apr_pstrndup (pool, beg, end - beg);
+ result = apr_pstrndup (pool, beg, end - beg);
+
+ /* Unquote vigorously */
+ if (quoted) {
+ at = from = result;
+ for (;;) {
+ if (from[0] == '\\' && from[1] != '\0')
+ *(at++) = *(++from);
+ else
+ *(at++) = *from;
+ if (*from == '\0')
+ break;
+ ++from;
+ }
+ }
+
+ return result;
}
/* -------------------------------------------------------------------------------
@@ -483,11 +512,6 @@ static const command_rec command_table[] = {
* COOKIE SESSIONS
*/
-typedef struct sid_session {
- char *identifier;
- time_t expiry;
-} sid_session_t;
-
/* Secret used to sign session cookies */
static unsigned char session_secret[40];
@@ -577,6 +601,7 @@ session_load_info (sid_context_t *ctx, request_rec *r)
const char *value;
char *token, *sig, *end;
char *identifier;
+ char **here;
long expiry;
value = session_cookie_value (r, ctx->cookie_name);
@@ -589,7 +614,7 @@ session_load_info (sid_context_t *ctx, request_rec *r)
/* The version of the session info, only 1 supported for now */
token = get_token (r->pool, &value, " ");
- if (!token || strcmp (token, "1") != 0)
+ if (!token || strcmp (token, "2") != 0)
return NULL;
token = get_token (r->pool, &value, " ");
@@ -610,20 +635,38 @@ session_load_info (sid_context_t *ctx, request_rec *r)
sess->expiry = expiry;
sess->identifier = identifier;
+ /* The remainder are values */
+ for (;;) {
+ token = get_token (r->pool, &value, " ");
+ if (!token)
+ break;
+ if (!sess->values)
+ sess->values = apr_array_make (r->pool, 8, sizeof (char*));
+ here = (char**)apr_array_push (sess->values);
+ *here = token;
+ }
+
return sess;
}
static void
session_send_info (sid_context_t *ctx, request_rec *r, sid_session_t *sess)
{
- char *cookie, *sig, *value;
+ char *cookie, *sig, *payload, *values;
+
+ /* All the values */
+ if (sess->values)
+ values = apr_array_pstrcat (r->pool, sess->values, ' ');
+ else
+ values = "";
/* Create the cookie value and sign it */
- value = apr_psprintf (r->pool, "1 %ld \"%s\"", (long)sess->expiry, ap_escape_quotes (r->pool, sess->identifier));
- sig = session_create_sig (r->pool, value);
+ payload = apr_psprintf (r->pool, "2 %ld \"%s\" %s", (long)sess->expiry,
+ ap_escape_quotes (r->pool, sess->identifier), values);
+ sig = session_create_sig (r->pool, payload);
/* Build up the full cookie spec */
- cookie = apr_psprintf (r->pool, "%s=%s %s; httponly; max-age=86400", ctx->cookie_name, sig, value);
+ cookie = apr_psprintf (r->pool, "%s=%s %s; httponly", ctx->cookie_name, sig, payload);
apr_table_addn (r->err_headers_out, "Set-Cookie", cookie);
}
@@ -631,8 +674,20 @@ static sid_session_t*
session_copy_info (apr_pool_t *p, sid_session_t *sess)
{
sid_session_t *copy = apr_pcalloc (p, sizeof (*sess));
+ char **data;
+ int i;
+
copy->expiry = sess->expiry;
copy->identifier = apr_pstrdup (p, sess->identifier);
+
+ /* Copy the values if necessary */
+ if (sess->values) {
+ copy->values = apr_array_copy (p, sess->values);
+ data = (char**)copy->values->elts;
+ for (i = 0; i < copy->values->nelts; ++i)
+ data[i] = apr_pstrdup (p, data[i]);
+ }
+
return copy;
}
@@ -643,9 +698,9 @@ session_copy_info (apr_pool_t *p, sid_session_t *sess)
struct sid_request {
int result;
request_rec *rec;
+ sid_session_t *sess;
};
-
void
sid_request_log_error (sid_request_t *req, const char *message, const char *detail)
{
@@ -714,81 +769,39 @@ sid_request_respond (sid_request_t *req, int code, const char *reason,
req->result = code;
}
-static void
-set_request_authenticated (sid_context_t *ctx, request_rec *r, sid_session_t *sess)
-{
- ap_regmatch_t matches[AP_MAX_REG_MATCH];
- char *user = NULL;
-
- /* Try and calculate a user name */
- switch (ctx->user_match) {
- case SUFFIX:
- if (ctx->identifier) {
- user = (char*)ap_stripprefix (sess->identifier, ctx->identifier);
- if (user != sess->identifier) {
- /* Some delimiters that we strip from between value and identifier */
- while (strchr ("?/#", *user))
- ++user;
- }
- }
- break;
-
- case REGEX:
- assert (ctx->converter);
- if (ap_regexec (ctx->converter, sess->identifier, AP_MAX_REG_MATCH, matches, 0) == 0)
- user = ap_pregsub (r->pool, "$1", sess->identifier, AP_MAX_REG_MATCH, matches);
- break;
- }
-
- if (!user)
- user = sess->identifier;
-
- r->user = user;
- r->ap_auth_type = SID_AUTHTYPE;
- apr_table_set (r->subprocess_env, "OPENID_IDENTIFIER", sess->identifier);
- ap_set_module_config (r->request_config, &auth_singleid_module, sess);
-}
-
void
sid_request_authenticated (sid_request_t *req, const char *identifier)
{
- sid_context_t *ctx;
sid_session_t *sess;
sess = apr_pcalloc (req->rec->pool, sizeof (sid_session_t));
sess->identifier = apr_pstrdup (req->rec->pool, identifier);
sess->expiry = time (NULL) + 86400;
-
- ctx = ap_get_module_config (req->rec->per_dir_config, &auth_singleid_module);
- assert (ctx);
-
- set_request_authenticated (ctx, req->rec, sess);
- session_send_info (ctx, req->rec, sess);
+ req->sess = sess;
}
void
-sid_request_attribute_values (sid_request_t *req, sid_attribute_t *attr, const char **values)
+sid_request_attribute_values (sid_request_t *req, sid_attribute_t *attr,
+ const char **values, size_t n_values)
{
+ sid_session_t *sess;
const char **val;
- char buf[64];
- char *name;
+ char **here;
int i = 0;
+ assert (req->sess);
+
+ /* Put all the values into the session array */
+ sess = req->sess;
+ if (!sess->values)
+ sess->values = apr_array_make (req->rec->pool, 8, sizeof (char*));
+
/* Setup all the values */
for (i = 0, val = values; *val; ++val, ++i) {
- snprintf (buf, sizeof (buf), "AX_VALUE_%s_%d", attr->alias, i);
- name = apr_pstrdup (req->rec->pool, buf);
- strupcase (name);
- apr_table_setn (req->rec->subprocess_env, name,
- apr_pstrdup (req->rec->pool, *val));
+ here = apr_array_push (sess->values);
+ *here = apr_psprintf (req->rec->pool, "%s:\"%s\"", attr->alias,
+ ap_escape_quotes (req->rec->pool, *val));
}
-
- /* Put in the count */
- snprintf (buf, sizeof (buf), "AX_COUNT_%s", attr->alias);
- name = apr_pstrdup (req->rec->pool, buf);
- strupcase (name);
- snprintf (buf, sizeof (buf), "%d", i);
- apr_table_set (req->rec->subprocess_env, name, apr_pstrdup (req->rec->pool, buf));
}
/* ---------------------------------------------------------------------------------------
@@ -813,6 +826,90 @@ hook_child (apr_pool_t *p, server_rec *s)
shared_child (p, s);
}
+static void
+set_authenticated (sid_context_t *ctx, request_rec *r, sid_session_t *sess)
+{
+ ap_regmatch_t matches[AP_MAX_REG_MATCH];
+ char *user = NULL;
+
+ /* Try and calculate a user name */
+ switch (ctx->user_match) {
+ case SUFFIX:
+ if (ctx->identifier) {
+ user = (char*)ap_stripprefix (sess->identifier, ctx->identifier);
+ if (user != sess->identifier) {
+ /* Some delimiters that we strip from between value and identifier */
+ while (strchr ("?/#", *user))
+ ++user;
+ }
+ }
+ break;
+
+ case REGEX:
+ assert (ctx->converter);
+ if (ap_regexec (ctx->converter, sess->identifier, AP_MAX_REG_MATCH, matches, 0) == 0)
+ user = ap_pregsub (r->pool, "$1", sess->identifier, AP_MAX_REG_MATCH, matches);
+ break;
+ }
+
+ if (!user)
+ user = apr_pstrdup (r->pool, sess->identifier);
+
+ r->user = user;
+ r->ap_auth_type = SID_AUTHTYPE;
+}
+
+static void
+set_env_variables (sid_context_t *ctx, request_rec *r, sid_session_t *sess)
+{
+ char *name, *value, *prev;
+ const char *line;
+ char **data;
+ int i, count;
+
+ apr_table_setn (r->subprocess_env, "OPENID_IDENTIFIER", sess->identifier);
+
+ if (sess->values) {
+ data = (char**)sess->values->elts;
+ prev = name = NULL;
+ count = 0;
+
+ for (i = 0; i < sess->values->nelts; ++i) {
+ line = data[i];
+
+ /* The two parts of the attribute value */
+ name = get_token (r->pool, &line, ":");
+ value = get_token (r->pool, &line, ":");
+ if (!name || !value)
+ continue;
+
+ strupcase (name);
+
+ /* Set the last count if necessary */
+ if (prev && strcmp (name, prev) != 0) {
+ apr_table_setn (r->subprocess_env,
+ apr_psprintf (r->pool, "AX_COUNT_%s", prev),
+ apr_psprintf (r->pool, "%d", count));
+ count = 0;
+ }
+
+ /* And dig out the value */
+ apr_table_setn (r->subprocess_env,
+ apr_psprintf (r->pool, "AX_VALUE_%s_%d", name, count), value);
+
+ prev = name;
+ ++count;
+ }
+
+ /* Set the last count if necessary */
+ if (name) {
+ apr_table_setn (r->subprocess_env,
+ apr_psprintf (r->pool, "AX_COUNT_%s", prev),
+ apr_psprintf (r->pool, "%d", count));
+ }
+ }
+}
+
static int
hook_authenticate (request_rec* r)
{
@@ -821,6 +918,7 @@ hook_authenticate (request_rec* r)
sid_request_t req;
const char* authtype;
request_rec* mainreq;
+ int authenticated = 0;
/* Make sure it's for us */
if (!(authtype = ap_auth_type (r)) || strcasecmp (SID_AUTHTYPE, authtype) != 0)
@@ -838,29 +936,40 @@ hook_authenticate (request_rec* r)
while (mainreq->prev != NULL)
mainreq = mainreq->prev;
+ req.result = OK;
+ req.rec = r;
+ req.sess = NULL;
+
/* Check if we've already authenticated this request */
sess = ap_get_module_config (mainreq->request_config, &auth_singleid_module);
if (sess != NULL) {
- if (mainreq != r) {
+ if (mainreq != r)
sess = session_copy_info (r->pool, sess);
- set_request_authenticated (ctx, r, sess);
- }
- return OK;
}
/* Load the session info from the request and see if we've authenticated */
- sess = session_load_info (ctx, r);
- if (sess != NULL) {
- set_request_authenticated (ctx, r, sess);
- return OK;
+ if (sess == NULL)
+ sess = session_load_info (ctx, r);
+
+ /* Allocate a new empty session info */
+ if (sess == NULL) {
+ /* Do the OpenID magic */
+ sid_consumer_authenticate (&req, ctx->store, ctx->trust_root,
+ ctx->identifier, ctx->attributes);
+ authenticated = 1;
+ sess = req.sess;
}
- req.result = OK;
- req.rec = r;
+ /* Setup the request response */
+ if (sess != NULL && req.result == OK) {
+ set_authenticated (ctx, r, sess);
+ set_env_variables (ctx, r, sess);
+ ap_set_module_config (r->request_config, &auth_singleid_module, sess);
- /* Do the OpenID magic */
- sid_consumer_authenticate (&req, ctx->store, ctx->trust_root,
- ctx->identifier, ctx->attributes);
+ /* If we actually authenticated the user, then set the cookie */
+ if (authenticated)
+ session_send_info (ctx, r, sess);
+ }
return req.result;
}
diff --git a/module/mod_auth_singleid.h b/module/mod_auth_singleid.h
index a81652d..61e2c91 100644
--- a/module/mod_auth_singleid.h
+++ b/module/mod_auth_singleid.h
@@ -42,7 +42,8 @@ void sid_request_authenticated (sid_request_t *req,
void sid_request_attribute_values (sid_request_t *req,
sid_attribute_t *attr,
- const char **values);
+ const char **values,
+ size_t n_values);
/* -----------------------------------------------------------------------------------
* STORAGE: Actually, communications white-board between processes/threads.