Initial code
authorSimo Sorce <simo@redhat.com>
Tue, 28 Jan 2014 02:26:55 +0000 (21:26 -0500)
committerSimo Sorce <simo@redhat.com>
Sat, 15 Feb 2014 19:25:03 +0000 (14:25 -0500)
Signed-off-by: Simo Sorce <simo@redhat.com>
src/mod_auth_gssapi.c [new file with mode: 0644]

diff --git a/src/mod_auth_gssapi.c b/src/mod_auth_gssapi.c
new file mode 100644 (file)
index 0000000..9296135
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+   MOD AUTH GSSAPI
+
+   Copyright (C) 2014 Simo Sorce <simo@redhat.com>
+
+   Permission is hereby granted, free of charge, to any person obtaining a
+   copy of this software and associated documentation files (the "Software"),
+   to deal in the Software without restriction, including without limitation
+   the rights to use, copy, modify, merge, publish, distribute, sublicense,
+   and/or sell copies of the Software, and to permit persons to whom the
+   Software is furnished to do so, subject to the following conditions:
+
+   The above copyright notice and this permission notice shall be included in
+   all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+   DEALINGS IN THE SOFTWARE.
+*/
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <gssapi/gssapi.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+#include <http_request.h>
+#include <apr_strings.h>
+#include <apr_base64.h>
+
+module AP_MODULE_DECLARE_DATA mag_module;
+
+struct mag_config {
+    bool ssl_only;
+    bool save_creds;
+};
+
+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;
+}
+
+static 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 int mag_auth(request_rec *req)
+{
+    const char *type;
+    struct mag_config *cfg;
+    const char *auth_header;
+    char *auth_header_type;
+    char *auth_header_value;
+    int ret = HTTP_UNAUTHORIZED;
+    gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+    gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
+    gss_name_t client = GSS_C_NO_NAME;
+    gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
+    uint32_t flags;
+    uint32_t maj, min;
+    char *reply;
+    size_t replen;
+
+    type = ap_auth_type(req);
+    if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
+        return DECLINED;
+    }
+
+    cfg = ap_get_module_config(req->per_dir_config, &mag_module);
+
+    /* FIXME: Checks for ssl only configuration */
+
+    auth_header = apr_table_get(req->headers_in, "Authorization");
+    if (!auth_header) goto done;
+
+    auth_header_type = ap_getword_white(req->pool, &auth_header);
+    if (!auth_header_type) goto done;
+
+    if (strcasecmp(auth_header_type, "Negotiate") != 0) goto done;
+
+    auth_header_value = ap_getword_white(req->pool, &auth_header);
+    if (!auth_header_value) goto done;
+    input.length = apr_base64_decode_len(auth_header_value) + 1;
+    input.value = apr_pcalloc(req->pool, input.length);
+    if (!input.value) goto done;
+    input.length = apr_base64_decode(input.value, auth_header_value);
+
+    /* FIXME: this works only with "one-roundtrip" gssapi auth for now,
+     * should work with Krb, will fail with NTLMSSP */
+    maj = gss_accept_sec_context(&min, &ctx, GSS_C_NO_CREDENTIAL,
+                                 &input, GSS_C_NO_CHANNEL_BINDINGS,
+                                 &client, NULL, &output, &flags, NULL,
+                                 &delegated_cred);
+    if (GSS_ERROR(maj)) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
+                      mag_error(req, "gss_accept_sec_context() failed",
+                                maj, min));
+        goto done;
+    }
+
+    if (output.length) {
+        replen = apr_base64_encode_len(output.length) + 1;
+        reply = apr_pcalloc(req->pool, 10 + replen);
+        if (!reply) goto done;
+        memcpy(reply, "Negotiate ", 10);
+        apr_base64_encode(&reply[10], output.value, output.length);
+        reply[replen] = '\0';
+        apr_table_add(req->err_headers_out, "WWW-Authenticate", reply);
+    }
+
+    maj = gss_display_name(&min, client, &name, NULL);
+    if (GSS_ERROR(maj)) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
+                      mag_error(req, "gss_accept_sec_context() failed",
+                                maj, min));
+        goto done;
+    }
+
+    /* FIXME: save creds */
+
+    req->ap_auth_type = "Negotiate";
+    req->user = apr_pstrndup(req->pool, name.value, name.length);
+    ret = OK;
+
+done:
+    if (ret == HTTP_UNAUTHORIZED) {
+        apr_table_add(req->err_headers_out, "WWW-Authenticate", "Negotiate");
+    }
+    gss_release_cred(&min, &delegated_cred);
+    gss_release_buffer(&min, &output);
+    gss_release_name(&min, &client);
+    gss_release_buffer(&min, &name);
+    gss_delete_sec_context(&min, &ctx, GSS_C_NO_BUFFER);
+    return ret;
+}
+
+
+static void *mag_create_dir_config(apr_pool_t *p, char *dir)
+{
+    struct mag_config *cfg;
+
+    cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
+    if (!cfg) return NULL;
+
+    return cfg;
+}
+
+static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
+{
+    struct mag_config *cfg = (struct mag_config *)mconfig;
+    cfg->ssl_only = on ? true : false;
+    return NULL;
+}
+
+static const char *mag_save_creds(cmd_parms *parms, void *mconfig, int on)
+{
+    struct mag_config *cfg = (struct mag_config *)mconfig;
+    cfg->save_creds = on ? true : false;
+    return NULL;
+}
+
+static const command_rec mag_commands[] = {
+    AP_INIT_FLAG("GSSSSLOnly", mag_ssl_only, NULL, OR_AUTHCFG,
+                  "Work only if connection is SSL Secured"),
+    AP_INIT_FLAG("GSSSaveCreds", mag_save_creds, NULL, OR_AUTHCFG,
+                  "Save credentials"),
+    { NULL }
+};
+
+static void
+mag_register_hooks(apr_pool_t *p)
+{
+    ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+module AP_MODULE_DECLARE_DATA auth_gssapi_module =
+{
+    STANDARD20_MODULE_STUFF,
+    mag_create_dir_config,
+    NULL,
+    NULL,
+    NULL,
+    mag_commands,
+    mag_register_hooks
+};