static bool mag_acquire_creds(request_rec *req, struct mag_config *cfg, gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *creds, gss_OID_set *actual_mechs) { uint32_t maj, min; #ifdef HAVE_CRED_STORE gss_const_key_value_set_t store = cfg->cred_store; maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE, desired_mechs, cred_usage, store, creds, actual_mechs, NULL); #else maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE, desired_mechs, cred_usage, creds, actual_mechs, NULL); #endif if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s", mag_error(req, "gss_acquire_cred[_from]() " "failed to get server creds", maj, min)); return false; } return true; }
static gss_cred_id_t acquire_cred_service(const char *service, gss_OID nametype, gss_OID_set oidset, gss_cred_usage_t usage, gss_const_key_value_set_t cred_store) { OM_uint32 major_status, minor_status; gss_cred_id_t cred_handle; OM_uint32 time_rec; gss_buffer_desc name_buffer; gss_name_t name = GSS_C_NO_NAME; if (service) { name_buffer.value = rk_UNCONST(service); name_buffer.length = strlen(service); major_status = gss_import_name(&minor_status, &name_buffer, nametype, &name); if (GSS_ERROR(major_status)) errx(1, "import_name failed"); } major_status = gss_acquire_cred_from(&minor_status, name, 0, oidset, usage, cred_store, &cred_handle, NULL, &time_rec); if (GSS_ERROR(major_status)) { warnx("acquire_cred failed: %s", gssapi_err(major_status, minor_status, GSS_C_NO_OID)); } else { print_time(time_rec); gss_release_cred(&minor_status, &cred_handle); } if (name != GSS_C_NO_NAME) gss_release_name(&minor_status, &name); if (GSS_ERROR(major_status)) exit(1); return cred_handle; }
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 create_krb5_cred(krb5_context ctx, char *realm, char *user, char *password, char *ktname, krb5_ccache *ccache, gss_cred_id_t *gsscred, char **errmsg) { int rc = 0, len = 0; unsigned int minor_stat = 0, major_stat = 0; const char *errmsg_tmp = NULL; const char *cctype = NULL; char *cname = NULL; krb5_ccache defcc = NULL; krb5_creds creds; krb5_principal princ = NULL; krb5_keytab keytab = NULL; gss_key_value_element_desc elems[2]; gss_key_value_set_desc store; gss_name_t sname = NULL; gss_buffer_desc pr_name; pr_name.value = NULL; pr_name.length = 0; store.count = 0; store.elements = elems; if (user == NULL || realm == NULL) return 1; len = strlen(realm); if (len == 0 || strlen(user) == 0) return 0; DEBUG("create_krb5_cred (ctx:%p, realm:%s, user:%s, password:%s, ktname: %s," " ccache:%p, gsscred:%p)", ctx, realm, user, "****", ktname, ccache, gsscred); rc = krb5_cc_default(ctx, &defcc); if (rc != 0) goto end; cctype = krb5_cc_get_type(ctx, defcc); rc = krb5_cc_new_unique(ctx, cctype, NULL, ccache); if (rc != 0) goto end; rc = krb5_build_principal(ctx, &princ, len, realm, user, NULL); if (rc != 0) goto end; rc = krb5_cc_initialize(ctx, *ccache, princ); if (rc != 0) goto end; if (password != NULL && strlen(password) > 0) { rc = krb5_get_init_creds_password(ctx, &creds, princ, password, 0, NULL, 0, NULL, NULL); if (rc != 0) goto end; rc = krb5_cc_store_cred(ctx, *ccache, &creds); if (rc != 0) goto end; rc = krb5_cc_get_full_name(ctx, *ccache, &cname); if (rc != 0) goto end; store.elements[store.count].key = "ccache"; store.elements[store.count].value = cname; store.count++; } if (ktname != NULL && strlen(ktname) > 0) { rc = krb5_kt_resolve(ctx, ktname, &keytab); if (rc != 0) goto end; rc = krb5_get_init_creds_keytab(ctx, &creds, princ, keytab, 0, NULL, NULL); if (rc != 0) goto end; rc = krb5_cc_store_cred(ctx, *ccache, &creds); if (rc != 0) goto end; rc = krb5_cc_get_full_name(ctx, *ccache, &cname); if (rc != 0) goto end; store.elements[store.count].key = "client_keytab"; store.elements[store.count].value = ktname; store.count++; store.elements[store.count].key = "ccache"; store.elements[store.count].value = cname; store.count++; rc = krb5_unparse_name(ctx, princ, (char**)&pr_name.value); if (rc != 0) goto end; pr_name.length = strlen(pr_name.value); major_stat = gss_import_name(&minor_stat, &pr_name, GSS_KRB5_NT_PRINCIPAL_NAME, &sname); if (major_stat != 0) goto end; } // Does not work with GSS-SPENGO. //major_stat = gss_krb5_import_cred(&minor_stat, *ccache, princ, NULL, gsscred); major_stat = gss_acquire_cred_from(&minor_stat, sname, 0, GSS_C_NO_OID_SET, GSS_C_INITIATE, &store, gsscred, NULL, NULL); end: if (keytab != NULL) krb5_kt_close(ctx, keytab); if (princ != NULL) krb5_free_principal(ctx, princ); if (defcc != NULL) krb5_cc_close(ctx, defcc); if (cname != NULL) free(cname); if (pr_name.value != NULL) krb5_free_unparsed_name(ctx, pr_name.value); if (sname != NULL) { major_stat = gss_release_name(&minor_stat, &sname); } if (rc != 0) { /* Create error message with the error code. */ errmsg_tmp = krb5_get_error_message(ctx, rc); if (errmsg != NULL && errmsg_tmp != NULL) { len = strlen(errmsg_tmp) + 26; *errmsg = (char *)malloc(len); if (*errmsg == NULL) { krb5_free_error_message(ctx, errmsg_tmp); return -1; } snprintf(*errmsg, len,"%s. (KRB5_ERROR 0x%08x)", errmsg_tmp, rc); } krb5_free_error_message(ctx, errmsg_tmp); } if (major_stat != 0) return major_stat; return rc; }
int main(int argc, char *argv[]) { OM_uint32 minor, major; gss_key_value_set_desc store; gss_buffer_desc buf; gss_name_t service = GSS_C_NO_NAME; gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; int i, e; if (argc < 2 || ((argc - 3) % 2)) { usage(argv[0]); exit(1); } store.count = (argc - 3) / 2; store.elements = calloc(store.count, sizeof(struct gss_key_value_element_struct)); if (!store.elements) { fprintf(stderr, "OOM\n"); exit(1); } if (argc > 2) { if (strcmp(argv[2], "--cred_store") != 0) { usage(argv[0]); exit(1); } for (i = 3, e = 0; i < argc; i += 2, e++) { store.elements[e].key = argv[i]; store.elements[e].value = argv[i + 1]; continue; } } /* First acquire default creds and try to store them in the cred store. */ major = gss_acquire_cred(&minor, GSS_C_NO_NAME, 0, GSS_C_NO_OID_SET, GSS_C_INITIATE, &cred, NULL, NULL); if (major) { print_status("gss_acquire_cred(default user creds) failed", major, minor); goto out; } major = gss_store_cred_into(&minor, cred, GSS_C_INITIATE, GSS_C_NO_OID, 1, 0, &store, NULL, NULL); if (major) { print_status("gss_store_cred_in_store(default user creds) failed", major, minor); goto out; } gss_release_cred(&minor, &cred); /* Then try to acquire creds from store. */ buf.value = argv[1]; buf.length = strlen(argv[1]); major = gss_import_name(&minor, &buf, (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, &service); if (major) { print_status("gss_import_name(principal) failed", major, minor); goto out; } major = gss_acquire_cred_from(&minor, service, 0, GSS_C_NO_OID_SET, GSS_C_BOTH, &store, &cred, NULL, NULL); if (major) { print_status("gss_acquire_cred_from_store(principal) failed", major, minor); goto out; } fprintf(stdout, "Cred Store Success\n"); major = 0; out: gss_release_name(&minor, &service); gss_release_cred(&minor, &cred); free(store.elements); return major; }