#include "mod_auth_gssapi.h"
void
-gss_log(const char *file,
+gss_log(const char *file,
int line,
#if AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER == 4
int module_index,
va_start(ap, fmt);
vsnprintf(errstr, sizeof(errstr), fmt, ap);
va_end(ap);
-
+
ap_log_rerror(file,
- line,
+ line,
#if AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER == 4
module_index,
#endif
- level | APLOG_NOERRNO,
- status,
- r,
- "%s",
+ level | APLOG_NOERRNO,
+ status,
+ r,
+ "%s",
errstr);
}
if (ctx && ctx->context != GSS_C_NO_CONTEXT)
gss_delete_sec_context(&minor_status, &ctx->context, GSS_C_NO_BUFFER);
-
+
if (ctx && ctx->server_creds != GSS_C_NO_CREDENTIAL)
gss_release_cred(&minor_status, &ctx->server_creds);
{
char key[1024];
gss_conn_ctx ctx = NULL;
-
+
snprintf(key, sizeof(key), "mod_auth_gssweb:conn_ctx");
-
- if (NULL == (ctx = (gss_conn_ctx) apr_palloc(r->connection->pool, sizeof(*ctx)))) {
+
+ if (NULL == (ctx = (gss_conn_ctx) apr_pcalloc(r->connection->pool, sizeof(*ctx)))) {
gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gss_create_conn_ctx: Can't allocate GSS context");
return NULL;
}
ctx->state = GSS_CTX_EMPTY;
ctx->filter_stat = GSS_FILT_NEW;
ctx->user = NULL;
+ ctx->name_attributes = NULL;
+ apr_pool_create(&ctx->pool, r->connection->pool);
+ /* register the context in the memory pool, so it can be freed
+ * when the connection/request is terminated */
+ apr_pool_cleanup_register(ctx->pool, (void *)ctx,
+ gss_cleanup_conn_ctx, apr_pool_cleanup_null);
/* Acquire and store server credentials */
if (0 == get_gss_creds(r, conf, &(ctx->server_creds))) {
{
char key[1024];
gss_conn_ctx ctx = NULL;
-
+
snprintf(key, sizeof(key), "mod_auth_gssweb:conn_ctx");
apr_pool_userdata_get((void **)&ctx, key, r->connection->pool);
gss_auth_config *conf;
conf = (gss_auth_config *) apr_pcalloc(p, sizeof(*conf));
+ conf->pool = p;
+
return conf;
}
const char *
get_gss_error(request_rec *r, OM_uint32 err_maj, OM_uint32 err_min, char *prefix)
{
- OM_uint32 maj_stat, min_stat;
+ OM_uint32 maj_stat, min_stat;
OM_uint32 msg_ctx = 0;
gss_buffer_desc status_string;
char *err_msg;
gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "Acquiring creds for %s", token.value);
gss_release_buffer(&minor_status, &token);
-
+
major_status = gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE,
GSS_C_NO_OID_SET, GSS_C_ACCEPT,
server_creds, NULL, NULL);
return memcmp(p, oid->elements, oid->length);
}
+/*
+ * Name attributes to environment variables code
+ * This code is strongly based on the code from https://github.com/modauthgssapi/mod_auth_gssapi
+ */
+
+static char *mag_status(request_rec *req, int type, uint32_t err)
+{
+ uint32_t maj_ret, min_ret;
+ gss_buffer_desc text;
+ uint32_t msg_ctx;
+ char *msg_ret;
+ int len;
+
+ msg_ret = NULL;
+ msg_ctx = 0;
+ do {
+ maj_ret = gss_display_status(&min_ret, err, type,
+ GSS_C_NO_OID, &msg_ctx, &text);
+ if (maj_ret != GSS_S_COMPLETE) {
+ return msg_ret;
+ }
+
+ len = text.length;
+ if (msg_ret) {
+ msg_ret = apr_psprintf(req->pool, "%s, %*s",
+ msg_ret, len, (char *)text.value);
+ } else {
+ msg_ret = apr_psprintf(req->pool, "%*s", len, (char *)text.value);
+ }
+ gss_release_buffer(&min_ret, &text);
+ } while (msg_ctx != 0);
+
+ return msg_ret;
+}
+
+char *mag_error(request_rec *req, const char *msg, uint32_t maj, uint32_t min)
+{
+ char *msg_maj;
+ char *msg_min;
+
+ msg_maj = mag_status(req, GSS_C_GSS_CODE, maj);
+ msg_min = mag_status(req, GSS_C_MECH_CODE, min);
+ return apr_psprintf(req->pool, "%s: [%s (%s)]", msg, msg_maj, msg_min);
+}
+
+
+static char mag_get_name_attr(request_rec *req,
+ gss_name_t name, name_attr *attr)
+{
+ uint32_t maj, min;
+
+ maj = gss_get_name_attribute(&min, name, &attr->name,
+ &attr->authenticated,
+ &attr->complete,
+ &attr->value,
+ &attr->display_value,
+ &attr->more);
+ if (GSS_ERROR(maj)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
+ "gss_get_name_attribute() failed on %.*s%s",
+ (int)attr->name.length, (char *)attr->name.value,
+ mag_error(req, "", maj, min));
+ return 0;
+ }
+
+ return 1;
+}
+
+static void mc_add_name_attribute(gss_conn_ctx_t *gss_ctx,
+ const char *name, const char *value)
+{
+ size_t size;
+
+ if (gss_ctx->na_count % 16 == 0) {
+ size = sizeof(mag_attr) * (gss_ctx->na_count + 16);
+ gss_ctx->name_attributes = realloc(gss_ctx->name_attributes, size);
+ if (!gss_ctx->name_attributes) apr_pool_abort_get(gss_ctx->pool)(ENOMEM);
+ }
+
+ gss_ctx->name_attributes[gss_ctx->na_count].name = apr_pstrdup(gss_ctx->pool, name);
+ gss_ctx->name_attributes[gss_ctx->na_count].value = apr_pstrdup(gss_ctx->pool, value);
+ gss_ctx->na_count++;
+}
+
+static void mag_set_env_name_attr(request_rec *req, gss_conn_ctx_t *gss_ctx,
+ name_attr *attr)
+{
+ char *value = "";
+ int len = 0;
+
+ /* Prefer a display_value, otherwise fallback to value */
+ if (attr->display_value.length != 0) {
+ len = attr->display_value.length;
+ value = (char *)attr->display_value.value;
+ } else if (attr->value.length != 0) {
+ len = apr_base64_encode_len(attr->value.length);
+ value = apr_pcalloc(req->pool, len);
+ len = apr_base64_encode(value,
+ (char *)attr->value.value,
+ attr->value.length);
+ }
+
+ if (attr->number == 1) {
+ mc_add_name_attribute(gss_ctx,
+ attr->env_name,
+ apr_psprintf(req->pool, "%.*s", len, value));
+ }
+ if (attr->more != 0 || attr->number > 1) {
+ mc_add_name_attribute(gss_ctx,
+ apr_psprintf(req->pool, "%s_%d",
+ attr->env_name, attr->number),
+ apr_psprintf(req->pool, "%.*s", len, value));
+ }
+ if (attr->more == 0 && attr->number > 1) {
+ mc_add_name_attribute(gss_ctx,
+ apr_psprintf(req->pool, "%s_N", attr->env_name),
+ apr_psprintf(req->pool, "%d", attr->number - 1));
+ }
+}
+
+static void mag_add_json_name_attr(request_rec *req, char first,
+ name_attr *attr, char **json)
+{
+ const char *value = "";
+ int len = 0;
+ char *b64value = NULL;
+ int b64len = 0;
+ const char *vstart = "";
+ const char *vend = "";
+ const char *vformat;
+
+ if (attr->value.length != 0) {
+ b64len = apr_base64_encode_len(attr->value.length);
+ b64value = apr_pcalloc(req->pool, b64len);
+ b64len = apr_base64_encode(b64value,
+ (char *)attr->value.value,
+ attr->value.length);
+ }
+ if (attr->display_value.length != 0) {
+ len = attr->display_value.length;
+ value = (const char *)attr->display_value.value;
+ }
+ if (attr->number == 1) {
+ *json = apr_psprintf(req->pool,
+ "%s%s\"%.*s\":{\"authenticated\":%s,"
+ "\"complete\":%s,"
+ "\"values\":[",
+ *json, (first ? "" : ","),
+ (int)attr->name.length, (char *)attr->name.value,
+ attr->authenticated ? "true" : "false",
+ attr->complete ? "true" : "false");
+ } else {
+ vstart = ",";
+ }
+
+ if (b64value) {
+ if (len) {
+ vformat = "%s%s{\"raw\":\"%s\",\"display\":\"%.*s\"}%s";
+ } else {
+ vformat = "%s%s{\"raw\":\"%s\",\"display\":%.*s}%s";
+ }
+ } else {
+ if (len) {
+ vformat = "%s%s{\"raw\":%s,\"display\":\"%.*s\"}%s";
+ } else {
+ vformat = "%s%s{\"raw\":%s,\"display\":%.*s}%s";
+ }
+ }
+
+ if (attr->more == 0) {
+ vend = "]}";
+ }
+
+ *json = apr_psprintf(req->pool, vformat, *json,
+ vstart,
+ b64value ? b64value : "null",
+ len ? len : 4, len ? value : "null",
+ vend);
+}
+
+gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
+
+void mag_get_name_attributes(request_rec *req, gss_auth_config *cfg,
+ gss_name_t name, gss_conn_ctx_t *gss_ctx)
+{
+ if (!cfg->name_attributes) {
+ return;
+ }
+
+ uint32_t maj, min;
+ gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
+ name_attr attr;
+ char *json = NULL;
+ char *error;
+ int count = 0;
+ int i, j;
+
+ maj = gss_inquire_name(&min, name, NULL, NULL, &attrs);
+ if (GSS_ERROR(maj)) {
+ error = mag_error(req, "gss_inquire_name() failed", maj, min);
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s", error);
+ apr_table_set(req->subprocess_env, "GSS_NAME_ATTR_ERROR", error);
+ return;
+ }
+
+ if (!attrs || attrs->count == 0) {
+ mc_add_name_attribute(gss_ctx, "GSS_NAME_ATTR_ERROR", "0 attributes found");
+ }
+
+ if (cfg->name_attributes->output_json) {
+
+ if (attrs) count = attrs->count;
+
+ json = apr_psprintf(req->pool,
+ "{\"name\":\"%s\",\"attributes\":{",
+ gss_ctx->user);
+ } else {
+ count = cfg->name_attributes->map_count;
+ }
+
+ for (i = 0; i < count; i++) {
+ memset(&attr, 0, sizeof(name_attr));
+
+ if (cfg->name_attributes->output_json) {
+ attr.name = attrs->elements[i];
+ for (j = 0; j < cfg->name_attributes->map_count; j++) {
+ if (strncmp(cfg->name_attributes->map[j].attr_name,
+ attrs->elements[i].value,
+ attrs->elements[i].length) == 0) {
+ attr.env_name = cfg->name_attributes->map[j].env_name;
+ break;
+ }
+ }
+ } else {
+ attr.name.length = strlen(cfg->name_attributes->map[i].attr_name);
+ attr.name.value = cfg->name_attributes->map[i].attr_name;
+ attr.env_name = cfg->name_attributes->map[i].env_name;
+ }
+
+ attr.number = 0;
+ attr.more = -1;
+ do {
+ char code = 0;
+ attr.number++;
+ attr.value = empty_buffer;
+ attr.display_value = empty_buffer;
+ if (!mag_get_name_attr(req, name, &attr)) break;
+
+ if (cfg->name_attributes->output_json) {
+ mag_add_json_name_attr(req, i == 0, &attr, &json);
+ }
+ if (attr.env_name) {
+ mag_set_env_name_attr(req, gss_ctx, &attr);
+ }
+
+ gss_release_buffer(&min, &attr.value);
+ gss_release_buffer(&min, &attr.display_value);
+ } while (attr.more != 0);
+ }
+
+ if (cfg->name_attributes->output_json) {
+ json = apr_psprintf(req->pool, "%s}}", json);
+ mc_add_name_attribute(gss_ctx, "GSS_NAME_ATTRS_JSON", json);
+ }
+}
+
+static void mag_set_name_attributes(request_rec *req, gss_conn_ctx_t *gss_ctx)
+{
+ int i = 0;
+ for (i = 0; i < gss_ctx->na_count; i++) {
+ apr_table_set(req->subprocess_env,
+ gss_ctx->name_attributes[i].name,
+ gss_ctx->name_attributes[i].value);
+ }
+}
+
+void mag_set_req_data(request_rec *req,
+ gss_auth_config *cfg,
+ gss_conn_ctx_t *gss_ctx)
+{
+ if (gss_ctx->name_attributes) {
+ mag_set_name_attributes(req, gss_ctx);
+ }
+}
+
+#define GSS_NAME_ATTR_USERDATA "GSS Name Attributes Userdata"
+
+static apr_status_t mag_name_attrs_cleanup(void *data)
+{
+ gss_auth_config *cfg = (gss_auth_config *)data;
+ free(cfg->name_attributes);
+ cfg->name_attributes = NULL;
+ return 0;
+}
+
+const char *mag_name_attrs(cmd_parms *parms, void *mconfig,
+ const char *w)
+{
+ gss_auth_config *cfg = (gss_auth_config *)mconfig;
+ void *tmp_na;
+ size_t size = 0;
+ char *p;
+ int c;
+
+ if (!cfg->name_attributes) {
+ size = sizeof(mag_name_attributes)
+ + (sizeof(mag_na_map) * 16);
+ } else if (cfg->name_attributes->map_count % 16 == 0) {
+ size = sizeof(mag_name_attributes)
+ + (sizeof(mag_na_map)
+ * (cfg->name_attributes->map_count + 16));
+ }
+ if (size) {
+ tmp_na = realloc(cfg->name_attributes, size);
+ if (!tmp_na) apr_pool_abort_get(cfg->pool)(ENOMEM);
+
+ if (cfg->name_attributes) {
+ size_t empty = (sizeof(mag_na_map) * 16);
+ memset(tmp_na + size - empty, 0, empty);
+ } else {
+ memset(tmp_na, 0, size);
+ }
+ cfg->name_attributes = (mag_name_attributes *)tmp_na;
+ apr_pool_userdata_setn(cfg, GSS_NAME_ATTR_USERDATA,
+ mag_name_attrs_cleanup, cfg->pool);
+ }
+
+
+ p = strchr(w, ' ');
+ if (p == NULL) {
+ if (strcmp(w, "json") == 0) {
+ cfg->name_attributes->output_json = 1;
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
+ "Invalid Name Attributes value [%s].", w);
+ }
+ return NULL;
+ }
+
+ c = cfg->name_attributes->map_count;
+ cfg->name_attributes->map[c].env_name = apr_pstrndup(cfg->pool, w, p-w);
+ p++;
+ cfg->name_attributes->map[c].attr_name = apr_pstrdup(cfg->pool, p);
+ cfg->name_attributes->map_count += 1;
+
+ return NULL;
+}
\ No newline at end of file
* NOTE: Portions of the code in this file were derived from example
* code distributed under the Apache 2.0 license:
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* This module implements the Apache server side of the GSSWeb
* authentiction type which allows Moonshot to be used for
* authentication in web applications. The module consists of two
* response content.
*
* This module uses a simple protocol between the client and server
- * to exchange GSS tokens and nonce information. The protocol is
+ * to exchange GSS tokens and nonce information. The protocol is
* described in the protocol.txt file included with module source.
*/
{ NULL }
};
-
+
#define DEFAULT_ENCTYPE "application/x-www-form-urlencoded"
#define GSS_MAX_TOKEN_SIZE 4096 //TBD -- check this value
-/* gssweb_read_req() -- reads the request data into a buffer
+/* gssweb_read_req() -- reads the request data into a buffer
*/
static int gssweb_read_req(request_rec *r, const char **rbuf, apr_off_t *size)
{
gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gssweb_get_post_data: Failed to set up client block");
return(rc);
}
-
+
if(ap_should_client_block(r)) {
char argsbuffer[HUGE_STRING_LEN];
apr_off_t rsize, len_read, rpos = 0;
input_token->value = NULL;
gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "gssweb_get_post_data: Entering function");
-
+
if(r->method_number != M_POST) {
gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gssweb_get_post_data: Request data is not a POST, declining.");
return DECLINED;
gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gssweb_get_post_data: Data read error, rc = %d", rc);
return rc;
}
-
- while(*data && (val = ap_getword(r->pool, &data, '&'))) {
+
+ while(*data && (val = ap_getword(r->pool, &data, '&'))) {
key = ap_getword(r->pool, &val, '=');
ap_unescape_url((char*)key);
ap_unescape_url((char*)val);
}
/* gssweb_authenticate_filter() -- Output filter for gssweb authentication.
- * Wraps original response in JSON -- adding JSON to the beginning of the
+ * Wraps original response in JSON -- adding JSON to the beginning of the
* response, escapes double quotes in the original response, and adds JSON
* to the end of the response. Handles responses that involve more than
* one filter call by maintaining state until an EOS bucket is received.
gss_log(APLOG_MARK, APLOG_DEBUG, 0, f->r, "Entering GSSWeb filter");
- /* Get the context from the request. If the context is NULL or
- * there is no outstanding request (no nonce set), just forward
- * all of the buckets as-is, because the client isn't gssweb
+ /* Get the context from the request. If the context is NULL or
+ * there is no outstanding request (no nonce set), just forward
+ * all of the buckets as-is, because the client isn't gssweb
*/
if ((NULL == (conn_ctx = gss_retrieve_conn_ctx(r))) ||
(0 == conn_ctx->nonce)) {
bkt_in != APR_BRIGADE_SENTINEL(brig_in);
bkt_in = APR_BUCKET_NEXT(bkt_in))
{
- if (NULL == (brig_out = apr_brigade_create(r->pool, c->bucket_alloc))) {
+ if (NULL == (brig_out = apr_brigade_create(r->pool, c->bucket_alloc))) {
apr_brigade_cleanup(brig_in);
return HTTP_INTERNAL_SERVER_ERROR;
}
}
/* Send opening JSON block */
- snprintf((char *)data, len+1024,
- "{\"gssweb\": {\n\"token\": \"%s\",\n\"nonce\": \"%d\"},\n\"application\": {\n\"data\": \"",
+ snprintf((char *)data, len+1024,
+ "{\"gssweb\": {\n\"token\": \"%s\",\n\"nonce\": \"%d\"},\n\"application\": {\n\"data\": \"",
stoken, conn_ctx->nonce);
gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "gssweb_authenticate_filter: Sending (%d bytes): %s", strlen(data), data);
-
+
bkt_out = apr_bucket_heap_create(data, strlen(data), apr_bucket_free,
c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(brig_out, bkt_out);
if(APR_BUCKET_IS_EOS(bkt_in))
{
/* create and add the JSON closing block */
-
+
if (NULL == (data = apr_bucket_alloc(1024, c->bucket_alloc))) {
gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gssweb_authenticate_filter: Unable to allocate space for closing JSON block");
apr_brigade_cleanup(brig_in);
/* Indicate that the next filter call is a new response */
conn_ctx->filter_stat = GSS_FILT_NEW;
-
+
/* set EOS in the outbound brigade */
bkt_eos = apr_bucket_eos_create(c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL (brig_out, bkt_eos);
-
+
/* set application type to 'application/json' */
apr_table_set(r->headers_out, "Content-Type", "application/json");
buf[enc_len] = '\0';
gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "gssweb_authenticate_filter: Sending (%d bytes)", enc_len);
APR_BRIGADE_INSERT_TAIL(brig_out, bkt_out);
-
+
/* Send the output brigade */
if (OK != (ret = ap_pass_brigade(f->next, brig_out))) {
apr_brigade_cleanup(brig_in);
* gssweb_insert_error_filter hook.
*/
static void
-gssweb_add_filter(request_rec *r)
+gssweb_add_filter(request_rec *r)
{
gss_conn_ctx conn_ctx = NULL;
* output token back to the client.
*/
static int
-gssweb_authenticate_user(request_rec *r)
+gssweb_authenticate_user(request_rec *r)
{
const char *auth_line = NULL;
char *auth_type = NULL;
conn_ctx->state = GSS_CTX_ESTABLISHED;
r->user = apr_pstrdup(r->pool, conn_ctx->user);
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);
+
ret = OK;
end:
if (delegated_cred)
gss_release_cred(&minor_status, &delegated_cred);
-
+
if ((release_output_token) && (output_token.length))
gss_release_buffer(&minor_status, &output_token);
-
+
if (client_name != GSS_C_NO_NAME)
gss_release_name(&minor_status, &client_name);