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