Example #1
0
OM_uint32 KRB5_CALLCONV
gss_pname_to_uid(OM_uint32 *minor,
                 const gss_name_t name,
                 const gss_OID mech_type,
                 uid_t *uidOut)
{
    OM_uint32 major = GSS_S_UNAVAILABLE, tmpminor;
#ifndef NO_PASSWORD
    gss_buffer_desc localname;
    char pwbuf[BUFSIZ];
    char *localuser = NULL;
    struct passwd *pwd = NULL;
    struct passwd pw;
    int code = 0;

    localname.value = NULL;
    major = gss_localname(minor, name, mech_type, &localname);
    if (!GSS_ERROR(major) && localname.value) {
        localuser = malloc(localname.length + 1);
        if (localuser == NULL)
            code = ENOMEM;
        if (code == 0) {
            memcpy(localuser, localname.value, localname.length);
            localuser[localname.length] = '\0';
            code = k5_getpwnam_r(localuser, &pw, pwbuf, sizeof(pwbuf), &pwd);
        }
        if ((code == 0) && pwd)
            *uidOut = pwd->pw_uid;
        else
            major = GSS_S_FAILURE;
    }
    free(localuser);
    if (localname.value)
        gss_release_buffer(&tmpminor, &localname);
#endif /*NO_PASSWORD*/
    return major;
}
Example #2
0
static pam_handle_t *
perform_gssapi (void)
{
  struct pam_conv conv = { pam_conv_func, };
  OM_uint32 major, minor;
  gss_cred_id_t server = GSS_C_NO_CREDENTIAL;
  gss_cred_id_t client = GSS_C_NO_CREDENTIAL;
  gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc local = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc display = GSS_C_EMPTY_BUFFER;
  gss_name_t name = GSS_C_NO_NAME;
  gss_ctx_id_t context = GSS_C_NO_CONTEXT;
  gss_OID mech_type = GSS_C_NO_OID;
  pam_handle_t *pamh = NULL;
  OM_uint32 flags = 0;
  char *str = NULL;
  OM_uint32 caps = 0;
  int res;

  server = GSS_C_NO_CREDENTIAL;
  res = PAM_AUTH_ERR;

  /* We shouldn't be writing to kerberos caches here */
  setenv ("KRB5CCNAME", "FILE:/dev/null", 1);

  debug ("reading kerberos auth from cockpit-ws");
  input.value = read_auth_until_eof (&input.length);

  major = gss_accept_sec_context (&minor, &context, server, &input,
                                  GSS_C_NO_CHANNEL_BINDINGS, &name, &mech_type,
                                  &output, &flags, &caps, &client);

  if (GSS_ERROR (major))
    {
      warnx ("gssapi auth failed: %s", gssapi_strerror (major, minor));
      goto out;
    }

  /*
   * In general gssapi mechanisms can require multiple challenge response
   * iterations keeping &context between each, however Kerberos doesn't
   * require this, so we don't care :O
   *
   * If we ever want this to work with something other than Kerberos, then
   * we'll have to have some sorta session that holds the context.
   */
  if (major & GSS_S_CONTINUE_NEEDED)
    goto out;

  major = gss_localname (&minor, name, mech_type, &local);
  if (major == GSS_S_COMPLETE)
    {
      minor = 0;
      str = dup_string (local.value, local.length);
      debug ("mapped gssapi name to local user '%s'", str);

      if (getpwnam (str))
        {
          res = pam_start ("cockpit", str, &conv, &pamh);
        }
      else
        {
          /* If the local user doesn't exist, pretend gss_localname() failed */
          free (str);
          str = NULL;
          major = GSS_S_FAILURE;
          minor = KRB5_NO_LOCALNAME;
        }
    }

  if (major != GSS_S_COMPLETE)
    {
      if (minor == (OM_uint32)KRB5_NO_LOCALNAME || minor == (OM_uint32)KRB5_LNAME_NOTRANS)
        {
          major = gss_display_name (&minor, name, &display, NULL);
          if (GSS_ERROR (major))
            {
              warnx ("couldn't get gssapi display name: %s", gssapi_strerror (major, minor));
              goto out;
            }

          str = dup_string (display.value, display.length);
          debug ("no local user mapping for gssapi name '%s'", str);

          res = pam_start ("cockpit", str, &conv, &pamh);
        }
      else
        {
          warnx ("couldn't map gssapi name to local user: %s", gssapi_strerror (major, minor));
          goto out;
        }
    }

  if (res != PAM_SUCCESS)
    errx (EX, "couldn't start pam: %s", pam_strerror (NULL, res));

  if (pam_set_item (pamh, PAM_RHOST, rhost) != PAM_SUCCESS ||
      pam_get_item (pamh, PAM_USER, (const void **)&user) != PAM_SUCCESS)
    errx (EX, "couldn't setup pam");

  assert (user != NULL);

  res = open_session (pamh, user);

out:
  write_auth_begin (res);
  if (user)
    write_auth_string ("user", user);
  if (output.value)
    write_auth_hex ("gssapi-output", output.value, output.length);

  if (output.value)
    gss_release_buffer (&minor, &output);
  output.value = NULL;
  output.length = 0;

  if (caps & GSS_C_DELEG_FLAG && client != GSS_C_NO_CREDENTIAL)
    {
#ifdef HAVE_GSS_IMPORT_CRED
      major = gss_export_cred (&minor, client, &output);
      if (GSS_ERROR (major))
        warnx ("couldn't export gssapi credentials: %s", gssapi_strerror (major, minor));
      else if (output.value)
        write_auth_hex ("gssapi-creds", output.value, output.length);
#else
      /* cockpit-ws will complain for us, if they're ever used */
      write_auth_hex ("gssapi-creds", (void *)"", 0);
#endif
    }

  write_auth_end ();

  if (display.value)
    gss_release_buffer (&minor, &display);
  if (output.value)
    gss_release_buffer (&minor, &output);
  if (local.value)
    gss_release_buffer (&minor, &local);
  if (client != GSS_C_NO_CREDENTIAL)
    gss_release_cred (&minor, &client);
  if (server != GSS_C_NO_CREDENTIAL)
    gss_release_cred (&minor, &server);
  if (name != GSS_C_NO_NAME)
     gss_release_name (&minor, &name);
  if (context != GSS_C_NO_CONTEXT)
     gss_delete_sec_context (&minor, &context, GSS_C_NO_BUFFER);
  free (input.value);
  free (str);

  unsetenv ("KRB5CCNAME");

  if (res != PAM_SUCCESS)
    exit (5);

  return pamh;
}
Example #3
0
static uint32_t gp_export_creds_linux(uint32_t *min, gss_name_t name,
                                      gss_const_OID mech, gss_buffer_t buf)
{
    gss_buffer_desc localname;
    uint32_t ret_maj;
    uint32_t ret_min;
    struct passwd pwd, *res;
    char *pwbuf = NULL;
    char *grbuf = NULL;
    uint32_t *p;
    size_t len;
    int count, num;
    int ret;

    /* We use gss_localname() to map the name. Then just use nsswitch to
     * look up the user.
     *
     * (TODO: If gss_localname() fails we may wanto agree with SSSD on a name
     * format to match principal names, es: gss:[email protected], or just
     * [email protected]) until sssd can provide a libkrb5 interface to augment
     * gss_localname() resolution for trusted realms */

    ret_maj = gss_localname(&ret_min, name, mech, &localname);
    if (ret_maj) {
        if (ret_min == ENOENT) {
            return gp_export_creds_enoent(min, buf);
        }
        *min = ret_min;
        return ret_maj;
    }

    len = 1024;
    pwbuf = malloc(len);
    if (!pwbuf) {
        ret_min = ENOMEM;
        ret_maj = GSS_S_FAILURE;
        goto done;
    }
    ret = 0;
    do {
        if (ret == ERANGE) {
            if (len == CREDS_BUF_MAX) {
                ret_min = ENOSPC;
                ret_maj = GSS_S_FAILURE;
                goto done;
            }
            len *= 2;
            if (len > CREDS_BUF_MAX) {
                len = CREDS_BUF_MAX;
            }
            p = realloc(pwbuf, len);
            if (!p) {
                ret_min = ENOMEM;
                ret_maj = GSS_S_FAILURE;
                goto done;
            }
            pwbuf = (char *)p;
        }
        ret = getpwnam_r((char *)localname.value, &pwd, pwbuf, len, &res);
    } while (ret == EINTR || ret == ERANGE);

    switch (ret) {
    case 0:
        if (res != NULL) {
            break;
        }
        /* fall through as ret == NULL is equivalent to ENOENT */
    case ENOENT:
    case ESRCH:
        free(pwbuf);
        return gp_export_creds_enoent(min, buf);
    default:
        ret_min = ret;
        ret_maj = GSS_S_FAILURE;
        goto done;
    }

    /* start with a reasonably sized buffer */
    count = 256;
    num = 0;
    do {
        if (count >= NGROUPS_MAX) {
            ret_min = ENOSPC;
            ret_maj = GSS_S_FAILURE;
            goto done;
        }
        count *= 2;
        if (count < num) {
            count = num;
        }
        if (count > NGROUPS_MAX) {
            count = NGROUPS_MAX;
        }
        len = count * sizeof(int32_t);
        p = realloc(grbuf, len + CREDS_HDR);
        if (!p) {
            ret_min = ENOMEM;
            ret_maj = GSS_S_FAILURE;
            goto done;
        }
        grbuf = (char *)p;
        num = count;
        ret = getgrouplist(pwd.pw_name, pwd.pw_gid, (gid_t *)&p[3], &num);
    } while (ret == -1);

    /* we got the buffer, now fill in [uid, gid, num] and we are done */
    p[0] = pwd.pw_uid;
    p[1] = pwd.pw_gid;
    p[2] = num;
    buf->value = p;
    buf->length = (num + 3) * sizeof(int32_t);
    ret_min = 0;
    ret_maj = GSS_S_COMPLETE;

done:
    if (ret_maj) {
       free(grbuf);
    }
    free(pwbuf);
    *min = ret_min;
    return ret_maj;
}
Example #4
0
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_ctx_id_t *pctx;
    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 acquired_cred = GSS_C_NO_CREDENTIAL;
    gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
    uint32_t flags;
    uint32_t vtime;
    uint32_t maj, min;
    char *reply;
    size_t replen;
    char *clientname;
    gss_OID mech_type = GSS_C_NO_OID;
    gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
    struct mag_conn *mc = NULL;

    type = ap_auth_type(req);
    if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
        return DECLINED;
    }

    /* ignore auth for subrequests */
    if (!ap_is_initial_req(req)) {
        return OK;
    }

    cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);

    if (cfg->ssl_only) {
        if (!mag_conn_is_https(req->connection)) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
                          "Not a TLS connection, refusing to authenticate!");
            goto done;
        }
    }

    if (cfg->gss_conn_ctx) {
        mc = (struct mag_conn *)ap_get_module_config(
                                                req->connection->conn_config,
                                                &auth_gssapi_module);
        if (!mc) {
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
                          "Failed to retrieve connection context!");
            goto done;
        }
    }

    /* if available, session always supersedes connection bound data */
    mag_check_session(req, cfg, &mc);

    if (mc) {
        /* register the context in the memory pool, so it can be freed
         * when the connection/request is terminated */
        apr_pool_userdata_set(mc, "mag_conn_ptr",
                              mag_conn_destroy, mc->parent);

        if (mc->established) {
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
                          "Already established context found!");
            apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
            req->ap_auth_type = apr_pstrdup(req->pool, "Negotiate");
            req->user = apr_pstrdup(req->pool, mc->user_name);
            ret = OK;
            goto done;
        }
        pctx = &mc->ctx;
    } else {
        pctx = &ctx;
    }

    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);

#ifdef HAVE_GSS_ACQUIRE_CRED_FROM
    if (cfg->use_s4u2proxy) {
        maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, 0,
                                    GSS_C_NO_OID_SET, GSS_C_BOTH,
                                    cfg->cred_store, &acquired_cred,
                                    NULL, NULL);
        if (GSS_ERROR(maj)) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
                          mag_error(req, "gss_acquire_cred_from() failed",
                                    maj, min));
            goto done;
        }
    }
#endif

    maj = gss_accept_sec_context(&min, pctx, acquired_cred,
                                 &input, GSS_C_NO_CHANNEL_BINDINGS,
                                 &client, &mech_type, &output, &flags, &vtime,
                                 &delegated_cred);
    if (GSS_ERROR(maj)) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
                      mag_error(req, "gss_accept_sec_context() failed",
                                maj, min));
        goto done;
    }

    if (maj == GSS_S_CONTINUE_NEEDED) {
        if (!mc) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
                          "Mechanism needs continuation but neither "
                          "GssapiConnectionBound nor "
                          "GssapiUseSessions are available");
            gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER);
            gss_release_buffer(&min, &output);
            output.length = 0;
        }
        /* auth not complete send token and wait next packet */
        goto done;
    }

    req->ap_auth_type = apr_pstrdup(req->pool, "Negotiate");

    /* Always set the GSS name in an env var */
    maj = gss_display_name(&min, client, &name, NULL);
    if (GSS_ERROR(maj)) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
                      mag_error(req, "gss_accept_sec_context() failed",
                                maj, min));
        goto done;
    }
    clientname = apr_pstrndup(req->pool, name.value, name.length);
    apr_table_set(req->subprocess_env, "GSS_NAME", clientname);

#ifdef HAVE_GSS_STORE_CRED_INTO
    if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
        char *ccachefile = NULL;

        mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
                              delegated_cred, &ccachefile);

        if (ccachefile) {
            apr_table_set(req->subprocess_env, "KRB5CCNAME", ccachefile);
        }
    }
#endif

    if (cfg->map_to_local) {
        maj = gss_localname(&min, client, mech_type, &lname);
        if (maj != GSS_S_COMPLETE) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
                          mag_error(req, "gss_localname() failed", maj, min));
            goto done;
        }
        req->user = apr_pstrndup(req->pool, lname.value, lname.length);
    } else {
        req->user = clientname;
    }

    if (mc) {
        mc->user_name = apr_pstrdup(mc->parent, req->user);
        mc->gss_name = apr_pstrdup(mc->parent, clientname);
        mc->established = true;
        if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
            vtime = MIN_SESS_EXP_TIME;
        }
        mc->expiration = time(NULL) + vtime;
        mag_attempt_session(req, cfg, mc);
    }

    ret = OK;

done:
    if (ret == HTTP_UNAUTHORIZED) {
        if (output.length != 0) {
            replen = apr_base64_encode_len(output.length) + 1;
            reply = apr_pcalloc(req->pool, 10 + replen);
            if (reply) {
                memcpy(reply, "Negotiate ", 10);
                apr_base64_encode(&reply[10], output.value, output.length);
                apr_table_add(req->err_headers_out,
                              "WWW-Authenticate", reply);
            }
        } else {
            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_release_buffer(&min, &lname);
    return ret;
}
Example #5
0
static int
pamGssInitAcceptSecContext(pam_handle_t *pamh,
                           int confFlags,
                           gss_cred_id_t cred,
                           gss_cred_id_t acceptorCred,
                           gss_name_t hostName,
                           gss_OID mech)
{
    int status;
    OM_uint32 major, minor;
    gss_buffer_desc initiatorToken = GSS_C_EMPTY_BUFFER;
    gss_buffer_desc acceptorToken = GSS_C_EMPTY_BUFFER;
    gss_ctx_id_t initiatorContext = GSS_C_NO_CONTEXT;
    gss_ctx_id_t acceptorContext = GSS_C_NO_CONTEXT;
    gss_buffer_desc canonUserNameBuf = GSS_C_EMPTY_BUFFER;
    gss_name_t canonUserName = GSS_C_NO_NAME;
    gss_OID canonMech = GSS_C_NO_OID;
    OM_uint32 gssFlags;

    do {
        major = gss_init_sec_context(&minor, cred, &initiatorContext,
                                     hostName, mech, GSS_C_MUTUAL_FLAG,
                                     GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS,
                                     &acceptorToken, NULL, &initiatorToken, &gssFlags, NULL);
        gss_release_buffer(&minor, &acceptorToken);

#ifdef GSS_S_PROMPTING_NEEDED
        if (major == GSS_S_PROMPTING_NEEDED) {
            status = PAM_CRED_INSUFFICIENT;
            goto cleanup;
        }
#endif

        BAIL_ON_GSS_ERROR(major, minor);

        if (initiatorToken.length != 0) {
            major = gss_accept_sec_context(&minor, &acceptorContext, acceptorCred,
                                           &initiatorToken, GSS_C_NO_CHANNEL_BINDINGS,
                                           &canonUserName, &canonMech, &acceptorToken,
                                           NULL, NULL, NULL);
            gss_release_buffer(&minor, &initiatorToken);
        }

        BAIL_ON_GSS_ERROR(major, minor);
    } while (major == GSS_S_CONTINUE_NEEDED);

    BAIL_ON_GSS_ERROR(major, minor);

    if ((gssFlags & GSS_C_MUTUAL_FLAG) == 0) {
        status = PAM_PERM_DENIED;
        goto cleanup;
    }

#ifndef __APPLE__
    major = gss_localname(&minor, canonUserName, GSS_C_NO_OID, &canonUserNameBuf);
    if (major == GSS_S_COMPLETE) {
        status = pam_set_item(pamh, PAM_USER, canonUserNameBuf.value);
        BAIL_ON_PAM_ERROR(status);
    } else if (major != GSS_S_UNAVAILABLE)
        goto cleanup;
#endif

    status = pam_set_data(pamh, GSS_NAME_DATA, canonUserName, pamGssCleanupName);
    BAIL_ON_PAM_ERROR(status);

    canonUserName = GSS_C_NO_NAME;

    status = pam_set_data(pamh, GSS_MECH_DATA, canonMech, pamGssCleanupMech);
    BAIL_ON_PAM_ERROR(status);

    canonMech = GSS_C_NO_OID;

    status = PAM_SUCCESS;

cleanup:
    gss_release_name(&minor, &canonUserName);
    gss_release_buffer(&minor, &initiatorToken);
    gss_release_buffer(&minor, &acceptorToken);
    gss_delete_sec_context(&minor, &initiatorContext, NULL);
    gss_delete_sec_context(&minor, &acceptorContext, NULL);
    gss_release_buffer(&minor, &canonUserNameBuf);

    if (IGNORE_ERR_P(status, confFlags))
        status = PAM_IGNORE;

    return status;
}
static int mag_auth(request_rec *req)
{
    const char *type;
    int auth_type = -1;
    struct mag_req_cfg *req_cfg;
    struct mag_config *cfg;
    const char *auth_header;
    char *auth_header_type;
    int ret = HTTP_UNAUTHORIZED;
    gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
    gss_ctx_id_t *pctx;
    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_buffer_desc ba_user;
    gss_buffer_desc ba_pwd;
    gss_name_t client = GSS_C_NO_NAME;
    gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
    gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
    gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
    uint32_t vtime;
    uint32_t maj, min;
    char *reply;
    size_t replen;
    gss_OID mech_type = GSS_C_NO_OID;
    gss_OID_set desired_mechs = GSS_C_NO_OID_SET;
    gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
    struct mag_conn *mc = NULL;
    int i;
    bool send_auth_header = true;

    type = ap_auth_type(req);
    if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
        return DECLINED;
    }

    req_cfg = mag_init_cfg(req);

    cfg = req_cfg->cfg;

    desired_mechs = req_cfg->desired_mechs;

    /* implicit auth for subrequests if main auth already happened */
    if (!ap_is_initial_req(req) && req->main != NULL) {
        type = ap_auth_type(req->main);
        if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) {
            /* warn if the subrequest location and the main request
             * location have different configs */
            if (cfg != ap_get_module_config(req->main->per_dir_config,
                                            &auth_gssapi_module)) {
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0,
                              req, "Subrequest authentication bypass on "
                                   "location with different configuration!");
            }
            if (req->main->user) {
                req->user = apr_pstrdup(req->pool, req->main->user);
                return OK;
            } else {
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
                              "The main request is tasked to establish the "
                              "security context, can't proceed!");
                return HTTP_UNAUTHORIZED;
            }
        } else {
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
                          "Subrequest GSSAPI auth with no auth on the main "
                          "request. This operation may fail if other "
                          "subrequests already established a context or the "
                          "mechanism requires multiple roundtrips.");
        }
    }

    if (cfg->ssl_only) {
        if (!mag_conn_is_https(req->connection)) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
                          "Not a TLS connection, refusing to authenticate!");
            goto done;
        }
    }

    if (cfg->gss_conn_ctx) {
        mc = (struct mag_conn *)ap_get_module_config(
                                                req->connection->conn_config,
                                                &auth_gssapi_module);
        if (!mc) {
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
                          "Failed to retrieve connection context!");
            goto done;
        }
    }

    /* if available, session always supersedes connection bound data */
    if (req_cfg->use_sessions) {
        mag_check_session(req_cfg, &mc);
    }

    auth_header = apr_table_get(req->headers_in, req_cfg->req_proto);

    if (mc) {
        if (mc->established &&
            (auth_header == NULL) &&
            (mc->auth_type != AUTH_TYPE_BASIC)) {
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
                          "Already established context found!");
            mag_set_req_data(req, cfg, mc);
            ret = OK;
            goto done;
        }
        pctx = &mc->ctx;
    } else {
        /* no preserved mc, create one just for this request */
        mc = mag_new_conn_ctx(req->pool);
        pctx = &ctx;
    }

    /* We can proceed only if we do have an auth header */
    if (!auth_header) goto done;

    auth_header_type = ap_getword_white(req->pool, &auth_header);
    if (!auth_header_type) goto done;

    /* We got auth header, sending auth header would mean re-auth */
    send_auth_header = !cfg->negotiate_once;

    for (i = 0; auth_types[i] != NULL; i++) {
        if (strcasecmp(auth_header_type, auth_types[i]) == 0) {
            auth_type = i;
            break;
        }
    }

    switch (auth_type) {
    case AUTH_TYPE_NEGOTIATE:
        if (!parse_auth_header(req->pool, &auth_header, &input)) {
            goto done;
        }
        break;
    case AUTH_TYPE_BASIC:
        if (!cfg->use_basic_auth) {
            goto done;
        }

        ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
        if (!ba_pwd.value) goto done;
        ba_user.value = ap_getword_nulls_nc(req->pool,
                                            (char **)&ba_pwd.value, ':');
        if (!ba_user.value) goto done;

        if (((char *)ba_user.value)[0] == '\0' ||
            ((char *)ba_pwd.value)[0] == '\0') {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
                          "Invalid empty user or password for Basic Auth");
            goto done;
        }
        ba_user.length = strlen(ba_user.value);
        ba_pwd.length = strlen(ba_pwd.value);

        if (mc->is_preserved && mc->established &&
            mag_basic_check(req_cfg, mc, ba_user, ba_pwd)) {
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
                          "Already established BASIC AUTH context found!");
            mag_set_req_data(req, cfg, mc);
            ret = OK;
            goto done;
        }

        break;

    case AUTH_TYPE_RAW_NTLM:
        if (!is_mech_allowed(desired_mechs, gss_mech_ntlmssp,
                             cfg->gss_conn_ctx)) {
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
                          "NTLM Authentication is not allowed!");
            goto done;
        }

        if (!parse_auth_header(req->pool, &auth_header, &input)) {
            goto done;
        }

        desired_mechs = discard_const(gss_mech_set_ntlmssp);
        break;

    default:
        goto done;
    }

    if (mc->established) {
        /* if we are re-authenticating make sure the conn context
         * is cleaned up so we do not accidentally reuse an existing
         * established context */
        mag_conn_clear(mc);
    }

    mc->auth_type = auth_type;

#ifdef HAVE_CRED_STORE
    if (use_s4u2proxy(req_cfg)) {
        cred_usage = GSS_C_BOTH;
    }
#endif

    if (auth_type == AUTH_TYPE_BASIC) {
        if (mag_auth_basic(req, cfg, ba_user, ba_pwd,
                           &client, &mech_type,
                           &delegated_cred, &vtime)) {
            goto complete;
        }
        goto done;
    }

    if (!mag_acquire_creds(req, cfg, desired_mechs,
                           cred_usage, &acquired_cred, NULL)) {
        goto done;
    }

    if (auth_type == AUTH_TYPE_NEGOTIATE &&
        cfg->allowed_mechs != GSS_C_NO_OID_SET) {
        maj = gss_set_neg_mechs(&min, acquired_cred, cfg->allowed_mechs);
        if (GSS_ERROR(maj)) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
                          mag_error(req, "gss_set_neg_mechs() failed",
                                    maj, min));
            goto done;
        }
    }

    maj = gss_accept_sec_context(&min, pctx, acquired_cred,
                                 &input, GSS_C_NO_CHANNEL_BINDINGS,
                                 &client, &mech_type, &output, NULL, &vtime,
                                 &delegated_cred);
    if (GSS_ERROR(maj)) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
                      mag_error(req, "gss_accept_sec_context() failed",
                                maj, min));
        goto done;
    } else if (maj == GSS_S_CONTINUE_NEEDED) {
        if (!mc->is_preserved) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
                          "Mechanism needs continuation but neither "
                          "GssapiConnectionBound nor "
                          "GssapiUseSessions are available");
            gss_release_buffer(&min, &output);
            output.length = 0;
        }
        /* auth not complete send token and wait next packet */
        goto done;
    }

complete:
    maj = gss_display_name(&min, client, &name, NULL);
    if (GSS_ERROR(maj)) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
                      mag_error(req, "gss_display_name() failed",
                                maj, min));
        goto done;
    }

    mc->gss_name = apr_pstrndup(req->pool, name.value, name.length);
    if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
        vtime = MIN_SESS_EXP_TIME;
    }
    mc->expiration = time(NULL) + vtime;

    mag_get_name_attributes(req, cfg, client, mc);

#ifdef HAVE_CRED_STORE
    if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
        char *ccache_path;

        mc->ccname = 0;
        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
                      "requester: %s", mc->gss_name);

        ccache_path = get_ccache_name(req, cfg->deleg_ccache_dir, mc->gss_name,
                                      cfg->deleg_ccache_unique, mc);
        if (ccache_path == NULL) {
            goto done;
        }

        mag_store_deleg_creds(req, ccache_path, delegated_cred);
        mc->delegated = true;

        if (!req_cfg->use_sessions && cfg->deleg_ccache_unique) {
            /* queue removing ccache to avoid littering filesystem */
            apr_pool_cleanup_register(mc->pool, ccache_path,
                                      (int (*)(void *)) unlink,
                                      apr_pool_cleanup_null);
        }

        /* extract filename from full path */
        mc->ccname = strrchr(ccache_path, '/') + 1;
    }
#endif

    if (cfg->map_to_local) {
        maj = gss_localname(&min, client, mech_type, &lname);
        if (maj != GSS_S_COMPLETE) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
                          mag_error(req, "gss_localname() failed", maj, min));
            goto done;
        }
        mc->user_name = apr_pstrndup(req->pool, lname.value, lname.length);
    } else {
        mc->user_name = apr_pstrdup(mc->pool, mc->gss_name);
    }

    mc->established = true;
    if (auth_type == AUTH_TYPE_BASIC) {
        mag_basic_cache(req_cfg, mc, ba_user, ba_pwd);
    }
    if (req_cfg->use_sessions) {
        mag_attempt_session(req_cfg, mc);
    }

    /* Now set request data and env vars */
    mag_set_req_data(req, cfg, mc);

    if (req_cfg->send_persist)
        apr_table_set(req->headers_out, "Persistent-Auth",
            cfg->gss_conn_ctx ? "true" : "false");

    ret = OK;

done:

    if ((auth_type != AUTH_TYPE_BASIC) && (output.length != 0)) {
        int prefixlen = strlen(mag_str_auth_type(auth_type)) + 1;
        replen = apr_base64_encode_len(output.length) + 1;
        reply = apr_pcalloc(req->pool, prefixlen + replen);
        if (reply) {
            memcpy(reply, mag_str_auth_type(auth_type), prefixlen - 1);
            reply[prefixlen - 1] = ' ';
            apr_base64_encode(&reply[prefixlen], output.value, output.length);
            apr_table_add(req->err_headers_out, req_cfg->rep_proto, reply);
        }
    } else if (ret == HTTP_UNAUTHORIZED) {
        if (send_auth_header) {
            apr_table_add(req->err_headers_out,
                          req_cfg->rep_proto, "Negotiate");
            if (is_mech_allowed(desired_mechs, gss_mech_ntlmssp,
                                cfg->gss_conn_ctx)) {
                apr_table_add(req->err_headers_out, req_cfg->rep_proto,
                              "NTLM");
            }
        }
        if (cfg->use_basic_auth) {
            apr_table_add(req->err_headers_out, req_cfg->rep_proto,
                          apr_psprintf(req->pool, "Basic realm=\"%s\"",
                                       ap_auth_name(req)));
        }
    }

    if (ctx != GSS_C_NO_CONTEXT)
        gss_delete_sec_context(&min, &ctx, GSS_C_NO_BUFFER);
    gss_release_cred(&min, &acquired_cred);
    gss_release_cred(&min, &delegated_cred);
    gss_release_buffer(&min, &output);
    gss_release_name(&min, &client);
    gss_release_buffer(&min, &name);
    gss_release_buffer(&min, &lname);
    return ret;
}