These should help developers to show nicer error messages to users (e.g. invalid credentials, not configured libraries, etc.).
+The module provides the following option to deal with GSS API name attributes:
+
#### GssapiNameAttributes
Enables the module to source Name Attributes from the client name
GssapiNameAttributes RADIUS_USER_NAME urn:ietf:params:gss:radius-attribute 1
GssapiNameAttributes EPPN urn:ietf:params:gss:federated-saml-attribute urn:oasis:names:tc:SAML:2.0:attrname-format:uri urn:oid:1.3.6.1.4.1.5923.1.1.1.6
+#### Feedback on authentication failure reason
+
+In addition to this, in the event of an authentication failure,
+ the module exports an environment variable called MAG_ERROR wich contains one
+of the following values:
+* "NO_AUTH_DATA" when the client did not send any authentication data (usually because the
+ appropriate libraries are not installed on the browser).
+* "UNSUP_AUTH_TYPE" when the client sent authentication data of an invalid type.
+* "GSS_MECH_ERROR" when the GSS mechanism failed for some reason (e.g. invalid credentials).
+
+In addition to this, whenever MAG_ERROR takes a value of "GSS_MECH_ERROR", an additional environment
+variable named GSS_ERROR_STR is sourced. This variable contains the result of
+the gss_display_status() call and may help web developers to show a more appropriate error page/string
+to the user.
+
static apr_status_t mag_gss_name_attrs_cleanup(void *data)
{
- gss_conn_ctx_t *gss_ctx = (struct gss_conn_ctx_t *)data;
+ gss_conn_ctx_t *gss_ctx = (gss_conn_ctx_t *) data;
free(gss_ctx->name_attributes);
gss_ctx->name_attributes = NULL;
return 0;
}
}
-static char* mag_escape_display_value(request_rec *req, gss_buffer_desc disp_value)
+static char *mag_escape_display_value(request_rec *req,
+ gss_buffer_desc disp_value)
{
- /* This function returns a copy (in the pool) of the given gss_buffer_t where every
- * occurrence of " has been replaced by \". This string is NULL terminated */
- int i = 0, j = 0, n_quotes = 0;
+ /* This function returns a copy (in the pool) of the given gss_buffer_t
+ * where some characters are escaped as required by RFC4627. The string is
+ * NULL terminated */
+ char *value = disp_value.value;
char *escaped_value = NULL;
- char *value = (char*) disp_value.value;
-
- // count number of quotes in the input string
- for (i = 0, j = 0; i < disp_value.length; i++)
- if (value[i] == '"')
- n_quotes++;
-
- // if there are no quotes, just return a copy of the string
- if (n_quotes == 0)
- return apr_pstrndup(req->pool, value, disp_value.length);
-
- // gss_buffer_t are not \0 terminated, but our result will be
- escaped_value = apr_palloc(req->pool, disp_value.length + n_quotes + 1);
- for (i = 0,j = 0; i < disp_value.length; i++, j++) {
- if (value[i] == '"') {
- escaped_value[j] = '\\';
- j++;
+ char *p = NULL;
+ size_t i = 0;
+
+ /* gss_buffer_t are not \0 terminated, but our result will be. Hence,
+ * escaped length will be original length * 6 + 1 in the worst case */
+ p = escaped_value = apr_palloc(req->pool, disp_value.length * 6 + 1);
+ for (i = 0; i < disp_value.length; i++) {
+ switch (value[i]) {
+ case '"':
+ memcpy(p, "\\\"", 2);
+ p += 2;
+ break;
+ case '\\':
+ memcpy(p, "\\\\", 2);
+ p += 2;
+ break;
+ case '\b':
+ memcpy(p, "\\b", 2);
+ p += 2;
+ break;
+ case '\t':
+ memcpy(p, "\\t", 2);
+ p += 2;
+ break;
+ case '\r':
+ memcpy(p, "\\r", 2);
+ p += 2;
+ break;
+ case '\f':
+ memcpy(p, "\\f", 2);
+ p += 2;
+ break;
+ case '\n':
+ memcpy(p, "\\n", 2);
+ p += 2;
+ break;
+ default:
+ if (value[i] <= 0x1F) {
+ apr_snprintf(p, 7, "\\u%04d", (int)value[i]);
+ p += 6;
+ } else {
+ *p = value[i];
+ p += 1;
+ }
+ break;
}
- escaped_value[j] = value[i];
}
- // make the string NULL terminated
- escaped_value[j] = '\0';
+ /* make the string NULL terminated */
+ *p = '\0';
return escaped_value;
}
}
if (GSS_ERROR(major_status)) {
- gss_log(APLOG_MARK, APLOG_ERR, 0, r,
- "%s", get_gss_error(r, major_status, minor_status,
- "Failed to establish authentication"));
+ // at least this error should be populated, to provider further information
+ // to the user (maybe)
+ char *error = get_gss_error(r, major_status, minor_status, "Failed to establish authentication");
+ apr_table_set(r->subprocess_env, "GSS_ERROR_STR", error);
+ gss_log(APLOG_MARK, APLOG_ERR, 0, r, error);
#if 0
/* Don't offer the Negotiate method again if call to GSS layer failed */
/* XXX ... which means we don't return the "error" output */
return OK;
}
+#define MAG_ERROR_NO_AUTH_DATA "NO_AUTH_DATA"
+#define MAG_ERROR_UNSUP_AUTH_TYPE "UNSUP_AUTH_TYPE"
+#define MAG_ERROR_GSS_MECH "GSS_MECH_ERROR"
+
static int
gss_authenticate_user(request_rec *r)
{
gss_log(APLOG_MARK, APLOG_DEBUG, 0, r,
"Client hasn't sent any authentication data, giving up");
set_http_headers(r, conf, "\0");
+ apr_table_set(r->subprocess_env, "MAG_ERROR", MAG_ERROR_NO_AUTH_DATA);
return HTTP_UNAUTHORIZED;
}
"Unsupported authentication type (%s) requested by client",
(auth_type) ? auth_type : "(NULL)");
set_http_headers(r, conf, "\0");
+ apr_table_set(r->subprocess_env, "MAG_ERROR", MAG_ERROR_UNSUP_AUTH_TYPE);
return HTTP_UNAUTHORIZED;
}
if (ret == OK) {
r->user = apr_pstrdup(r->pool, conn_ctx->user);
r->ap_auth_type = "Negotiate";
+ } else {
+ apr_table_set(r->subprocess_env, "MAG_ERROR", MAG_ERROR_GSS_MECH);
}
-
/* debug LOG ??? */
return ret;
r->ap_auth_type = "GSSWeb";
/* TODO: Make it a single call! */
- mag_get_name_attributes(r, conf, client_name, conn_ctx);
- mag_set_req_data(r, conf, conn_ctx);
+ if (conf->name_attributes) {
+ mag_get_name_attributes(r, conf, client_name, conn_ctx);
+ mag_set_req_data(r, conf, conn_ctx);
+ }
ret = OK;