* 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;
/* gssweb_get_post_data() -- Gets the token and nonce from the request
* data.
*/
-static int gssweb_get_post_data(request_rec *r, int *nonce, gss_buffer_desc *input_token)
+static int gssweb_get_post_data(request_rec *r, unsigned int *nonce, gss_buffer_desc *input_token)
{
const char *data;
apr_off_t datalen;
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;
}
type = apr_table_get(r->headers_in, "Content-Type");
- if(strcasecmp(type, DEFAULT_ENCTYPE) != 0) {
+ if(strncasecmp(type, DEFAULT_ENCTYPE, strlen(DEFAULT_ENCTYPE)) != 0) {
gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gssweb_get_post_data: Unexpected content type, 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;
}
return OK;
}
+ c_type = apr_table_get(r->headers_in, "Content-Type");
+ c_len = apr_table_get(r->headers_in, "Content-Length");
+ /* clear content-length and MD5 checksum */
+ apr_table_unset(r->headers_out, "Content-Length");
+ apr_table_unset(r->headers_out, "Content-MD5");
+ gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "gssweb_authenticate_filter: Received Content-Type: %s, Content-Length: %d", c_type, c_len);
+
/* If this is the first call for a response, send opening JSON block */
if (GSS_FILT_NEW == conn_ctx->filter_stat) {
gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "gssweb_authenticate_filter: First filter call for response");
}
/* 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);
return HTTP_INTERNAL_SERVER_ERROR;
}
- c_type = apr_table_get(r->headers_in, "Content-Type");
- c_len = apr_table_get(r->headers_in, "Content-Length");
snprintf((char *)data, 1024, "\",\n\"content-type\": \"%s\",\n\"content-length\": \"%s\"\n}\n}", c_type, c_len);
gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "gssweb_authenticate_filter: Sending (%d bytes) %s", strlen(data), data);
/* 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");
+
+ /* clear content-length and MD5 checksum */
+ apr_table_unset(r->headers_out, "Content-Length");
+ apr_table_unset(r->headers_out, "Content-MD5");
+
/* pass the brigade */
gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "gssweb_authenticate_filter: Sending: EOS");
if (0 != (ret = ap_pass_brigade(f->next, brig_out))) {
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;
/* Read the token and nonce from the POST */
if (0 != gssweb_get_post_data(r, &nonce, &input_token)) {
- gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gssweb_authenticate_user: Unable to read nonce or input token from GSSWeb input");
- gss_delete_sec_context(&minor_status, &conn_ctx->context, GSS_C_NO_BUFFER);
- conn_ctx->context = GSS_C_NO_CONTEXT;
- conn_ctx->state = GSS_CTX_FAILED;
- if (0 != conn_ctx->output_token.length)
- gss_release_buffer(&minor_status, &(conn_ctx->output_token));
- conn_ctx->output_token.length = 0;
- ret = HTTP_UNAUTHORIZED;
+ /* If we get spurious msg on an established session, say OK again */
+ if (GSS_CTX_ESTABLISHED == conn_ctx->state)
+ ret = OK;
+ /* ...otherwise, if we are in progress, return HTTP_UNAUTHORIZED */
+ if (GSS_CTX_IN_PROGRESS == conn_ctx->state)
+ ret = HTTP_UNAUTHORIZED;
+ /* If this would start a new session, free the context and return DECLINED */
+ else {
+ gss_cleanup_conn_ctx(conn_ctx);
+ ret = DECLINED;
+ }
goto end;
}
gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "gssweb_authenticate_user: GSSWeb nonce value = %u.", nonce);
"%s", get_gss_error(r, major_status, minor_status,
"gssweb_authenticate_user: Failed to establish authentication"));
conn_ctx->state = GSS_CTX_FAILED;
+ goto end;
}
/* If there was no token returned, clear token from context and exit */
release_output_token = 0;
/* If we aren't done yet, go around again */
- gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "gssweb_authenticate_user: Accept sec context complete, continue needed");
if (major_status & GSS_S_CONTINUE_NEEDED) {
+ gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "gssweb_authenticate_user: Accept sec context complete, continue needed");
conn_ctx->state = GSS_CTX_IN_PROGRESS;
ret = HTTP_UNAUTHORIZED;
goto end;
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! */
+ if (conf->name_attributes) {
+ 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);