/* * Depending on the version of Kerberos, we either need to use * a private function, or simply set the environment variable. */ static void gssd_set_krb5_ccache_name(char *ccname) { #ifdef USE_GSS_KRB5_CCACHE_NAME u_int maj_stat, min_stat; printerr(2, "using gss_krb5_ccache_name to select krb5 ccache %s\n", ccname); maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL); if (maj_stat != GSS_S_COMPLETE) { printerr(0, "WARNING: gss_krb5_ccache_name with " "name '%s' failed (%s)\n", ccname, error_message(min_stat)); } #else /* * Set the KRB5CCNAME environment variable to tell the krb5 code * which credentials cache to use. (Instead of using the private * function above for which there is no generic gssapi * equivalent.) */ printerr(2, "using environment variable to select krb5 ccache %s\n", ccname); setenv("KRB5CCNAME", ccname, 1); #endif }
int main(int argc, char **argv) { int optidx = 0; OM_uint32 flag; gss_OID type; setprogname(argv[0]); if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) usage(1); if (help_flag) usage (0); if(version_flag){ print_version(NULL); exit(0); } argc -= optidx; argv += optidx; if (argc != 0) usage(1); if (acquire_type) { if (strcasecmp(acquire_type, "both") == 0) flag = GSS_C_BOTH; else if (strcasecmp(acquire_type, "accept") == 0) flag = GSS_C_ACCEPT; else if (strcasecmp(acquire_type, "initiate") == 0) flag = GSS_C_INITIATE; else errx(1, "unknown type %s", acquire_type); } else flag = GSS_C_ACCEPT; if (name_type) { if (strcasecmp("hostbased-service", name_type) == 0) type = GSS_C_NT_HOSTBASED_SERVICE; else if (strcasecmp("user-name", name_type) == 0) type = GSS_C_NT_USER_NAME; else errx(1, "unknown name type %s", name_type); } else type = GSS_C_NT_HOSTBASED_SERVICE; if (ccache) { OM_uint32 major_status, minor_status; major_status = gss_krb5_ccache_name(&minor_status, ccache, NULL); if (GSS_ERROR(major_status)) errx(1, "gss_krb5_ccache_name %s", gssapi_err(major_status, minor_status, GSS_C_NO_OID)); } acquire_cred_service(acquire_name, type, flag); return 0; }
DWORD LwKrb5SetThreadDefaultCachePath( IN PCSTR pszCachePath, OUT PSTR* ppszPreviousCachePath ) { DWORD dwError = 0; DWORD dwMajorStatus = 0; DWORD dwMinorStatus = 0; PSTR pszOrigCachePath = NULL; // Set the default for gss dwMajorStatus = gss_krb5_ccache_name( (OM_uint32 *)&dwMinorStatus, pszCachePath, (ppszPreviousCachePath) ? (const char**)&pszOrigCachePath : NULL); BAIL_ON_GSS_ERROR(dwError, dwMajorStatus, dwMinorStatus); LW_LOG_DEBUG("Switched gss krb5 credentials path from %s to %s", LW_SAFE_LOG_STRING(pszOrigCachePath), LW_SAFE_LOG_STRING(pszCachePath)); if (ppszPreviousCachePath) { if (!LW_IS_NULL_OR_EMPTY_STR(pszOrigCachePath)) { dwError = LwAllocateString(pszOrigCachePath, ppszPreviousCachePath); BAIL_ON_LW_ERROR(dwError); } else { *ppszPreviousCachePath = NULL; } } cleanup: return dwError; error: if (ppszPreviousCachePath) { *ppszPreviousCachePath = NULL; } goto cleanup; }
DWORD SMBKrb5SetDefaultCachePath( PCSTR pszCachePath, PSTR* ppszOrigCachePath ) { DWORD dwError = 0; DWORD dwMajorStatus = 0; DWORD dwMinorStatus = 0; PSTR pszOrigCachePath = NULL; // Set the default for gss dwMajorStatus = gss_krb5_ccache_name( (OM_uint32 *)&dwMinorStatus, pszCachePath, (ppszOrigCachePath) ? (const char**)&pszOrigCachePath : NULL); BAIL_ON_SEC_ERROR(dwMajorStatus); if (ppszOrigCachePath) { if (!IsNullOrEmptyString(pszOrigCachePath)) { dwError = SMBAllocateString(pszOrigCachePath, ppszOrigCachePath); BAIL_ON_LWIO_ERROR(dwError); } else { *ppszOrigCachePath = NULL; } } LWIO_LOG_DEBUG("Cache path set to [%s]", LWIO_SAFE_LOG_STRING(pszCachePath)); cleanup: return dwError; sec_error: error: if (ppszOrigCachePath) { *ppszOrigCachePath = NULL; } goto cleanup; }
/* Acquire GSSAPI credentials and set up RPC auth flavor. */ static kadm5_ret_t setup_gss(kadm5_server_handle_t handle, kadm5_config_params *params_in, krb5_principal client, krb5_principal server) { OM_uint32 gssstat, minor_stat; gss_buffer_desc buf; gss_name_t gss_client; gss_name_t gss_target; gss_cred_id_t gss_client_creds; const char *c_ccname_orig; char *ccname_orig; gss_client_creds = GSS_C_NO_CREDENTIAL; ccname_orig = NULL; gss_client = gss_target = GSS_C_NO_NAME; /* Temporarily use the kadm5 cache. */ gssstat = gss_krb5_ccache_name(&minor_stat, handle->cache_name, &c_ccname_orig); if (gssstat != GSS_S_COMPLETE) goto error; if (c_ccname_orig) ccname_orig = strdup(c_ccname_orig); else ccname_orig = 0; buf.value = &server; buf.length = sizeof(server); gssstat = gss_import_name(&minor_stat, &buf, (gss_OID)gss_nt_krb5_principal, &gss_target); if (gssstat != GSS_S_COMPLETE) goto error; if (client != NULL) { buf.value = &client; buf.length = sizeof(client); gssstat = gss_import_name(&minor_stat, &buf, (gss_OID)gss_nt_krb5_principal, &gss_client); } else gss_client = GSS_C_NO_NAME; if (gssstat != GSS_S_COMPLETE) goto error; gssstat = gss_acquire_cred(&minor_stat, gss_client, 0, GSS_C_NULL_OID_SET, GSS_C_INITIATE, &gss_client_creds, NULL, NULL); if (gssstat != GSS_S_COMPLETE) { #if 0 /* for debugging only */ { OM_uint32 maj_status, min_status, message_context = 0; gss_buffer_desc status_string; do { maj_status = gss_display_status(&min_status, gssstat, GSS_C_GSS_CODE, GSS_C_NO_OID, &message_context, &status_string); if (maj_status == GSS_S_COMPLETE) { fprintf(stderr, "MAJ: %.*s\n", (int) status_string.length, (char *)status_string.value); gss_release_buffer(&min_status, &status_string); } else { fprintf(stderr, "MAJ? gss_display_status returns 0x%lx?!\n", (unsigned long) maj_status); message_context = 0; } } while (message_context != 0); do { maj_status = gss_display_status(&min_status, minor_stat, GSS_C_MECH_CODE, GSS_C_NO_OID, &message_context, &status_string); if (maj_status == GSS_S_COMPLETE) { fprintf(stderr, "MIN: %.*s\n", (int) status_string.length, (char *)status_string.value); gss_release_buffer(&min_status, &status_string); } else { fprintf(stderr, "MIN? gss_display_status returns 0x%lx?!\n", (unsigned long) maj_status); message_context = 0; } } while (message_context != 0); } #endif goto error; } /* * Do actual creation of RPC auth handle. Implements auth flavor * fallback. */ rpc_auth(handle, params_in, gss_client_creds, gss_target); error: if (gss_client_creds != GSS_C_NO_CREDENTIAL) (void) gss_release_cred(&minor_stat, &gss_client_creds); if (gss_client) gss_release_name(&minor_stat, &gss_client); if (gss_target) gss_release_name(&minor_stat, &gss_target); /* Revert to prior gss_krb5 ccache. */ if (ccname_orig) { gssstat = gss_krb5_ccache_name(&minor_stat, ccname_orig, NULL); if (gssstat) { return KADM5_GSS_ERROR; } free(ccname_orig); } else { gssstat = gss_krb5_ccache_name(&minor_stat, NULL, NULL); if (gssstat) { return KADM5_GSS_ERROR; } } if (handle->clnt->cl_auth == NULL) { return KADM5_GSS_ERROR; } return 0; }
static kbrccache_t userinitcontext( const char * user, const char * domain, const char * passwd, const char * cachename, int initialize, int * outError ) { krb5_context kcontext = 0; krb5_ccache kcache = 0; krb5_creds kcreds; krb5_principal kme = 0; krb5_error_code kres; char * pPass = strdup( passwd ); char * pName = NULL; char * pCacheName = NULL; int numCreds = 0; memset( &kcreds, 0, sizeof(kcreds) ); kres = krb5_init_context( &kcontext ); if( kres ) goto return_error; if( domain ) kres = krb5_build_principal( kcontext, &kme, strlen(domain), domain, user, (char *) 0 ); else kres = krb5_parse_name( kcontext, user, &kme ); if( kres ) goto fail; krb5_unparse_name( kcontext, kme, &pName ); if( cachename ) { if (asprintf(&pCacheName, "%s%s", cachename, pName) < 0) { kres = KRB5_CC_NOMEM; goto fail; } kres = krb5_cc_resolve( kcontext, pCacheName, &kcache ); if( kres ) { kres = krb5_cc_resolve( kcontext, CCACHE_PREFIX_DEFAULT, &kcache ); if( kres == 0 ) pCacheName = strdup(CCACHE_PREFIX_DEFAULT); } } else { kres = krb5_cc_default( kcontext, &kcache ); pCacheName = strdup( krb5_cc_get_name( kcontext, kcache ) ); } if( kres ) { krb5_free_context(kcontext); goto return_error; } if( initialize ) krb5_cc_initialize( kcontext, kcache, kme ); if( kres == 0 && user && passwd ) { long timeneeded = time(0L) +TKTTIMELEFT; int have_credentials = 0; krb5_cc_cursor cc_curs = NULL; numCreds = 0; if( (kres=krb5_cc_start_seq_get(kcontext, kcache, &cc_curs)) >= 0 ) { while( (kres=krb5_cc_next_cred(kcontext, kcache, &cc_curs, &kcreds))== 0) { numCreds++; if( krb5_principal_compare( kcontext, kme, kcreds.client ) ) { if( kcreds.ticket_flags & TKT_FLG_INITIAL && kcreds.times.endtime>timeneeded ) have_credentials = 1; } krb5_free_cred_contents( kcontext, &kcreds ); if( have_credentials ) break; } krb5_cc_end_seq_get( kcontext, kcache, &cc_curs ); } else { const char * errmsg = error_message(kres); fprintf( stderr, "%s user init(%s): %s\n", "setpass", pName, errmsg ); } if( kres != 0 || have_credentials == 0 ) { krb5_get_init_creds_opt *options = NULL; kres = krb5_get_init_creds_opt_alloc(kcontext, &options); if ( kres == 0 ) { get_init_creds_opt_init(options); /* ** no valid credentials - get new ones */ kres = krb5_get_init_creds_password( kcontext, &kcreds, kme, pPass, NULL /*prompter*/, NULL /*data*/, 0 /*starttime*/, 0 /*in_tkt_service*/, options /*options*/ ); } if( kres == 0 ) { if( numCreds <= 0 ) kres = krb5_cc_initialize( kcontext, kcache, kme ); if( kres == 0 ) kres = krb5_cc_store_cred( kcontext, kcache, &kcreds ); if( kres == 0 ) have_credentials = 1; } krb5_get_init_creds_opt_free(kcontext, options); } #ifdef NOTUSED if( have_credentials ) { int mstat; kres = gss_krb5_ccache_name( &mstat, pCacheName, NULL ); if( getenv( ENV_DEBUG_LDAPKERB ) ) fprintf( stderr, "gss credentials cache set to %s(%d)\n", pCacheName, kres ); } #endif krb5_cc_close( kcontext, kcache ); } fail: if( kres ) { const char * errmsg = error_message(kres); fprintf( stderr, "%s user init(%s): %s\n", "setpass", pName, errmsg ); } krb5_free_principal( kcontext, kme ); krb5_free_cred_contents( kcontext, &kcreds ); if( pName ) free( pName ); free(pPass); krb5_free_context(kcontext); return_error: if( kres ) { if( pCacheName ) { free(pCacheName); pCacheName = NULL; } } if( outError ) *outError = kres; return pCacheName; }
int main(int argc, char **argv) { gss_OID_set oidset = GSS_C_NULL_OID_SET; gss_OID mechoid = GSS_C_NO_OID; OM_uint32 maj_stat, min_stat; gss_cred_id_t cred; gss_name_t target = GSS_C_NO_NAME; int i, optidx = 0; OM_uint32 flag; gss_OID type; setprogname(argv[0]); if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) usage(1); if (help_flag) usage (0); if(version_flag){ print_version(NULL); exit(0); } argc -= optidx; argv += optidx; if (argc != 0) usage(1); if (acquire_type) { if (strcasecmp(acquire_type, "both") == 0) flag = GSS_C_BOTH; else if (strcasecmp(acquire_type, "accept") == 0) flag = GSS_C_ACCEPT; else if (strcasecmp(acquire_type, "initiate") == 0) flag = GSS_C_INITIATE; else errx(1, "unknown type %s", acquire_type); } else flag = GSS_C_ACCEPT; if (name_type) { if (strcasecmp("hostbased-service", name_type) == 0) type = GSS_C_NT_HOSTBASED_SERVICE; else if (strcasecmp("user-name", name_type) == 0) type = GSS_C_NT_USER_NAME; else errx(1, "unknown name type %s", name_type); } else type = GSS_C_NT_HOSTBASED_SERVICE; if (ccache) { maj_stat = gss_krb5_ccache_name(&min_stat, ccache, NULL); if (GSS_ERROR(maj_stat)) errx(1, "gss_krb5_ccache_name %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); } if (kerberos_flag) { mechoid = GSS_KRB5_MECHANISM; maj_stat = gss_create_empty_oid_set(&min_stat, &oidset); if (maj_stat != GSS_S_COMPLETE) errx(1, "gss_create_empty_oid_set: %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); maj_stat = gss_add_oid_set_member(&min_stat, GSS_KRB5_MECHANISM, &oidset); if (maj_stat != GSS_S_COMPLETE) errx(1, "gss_add_oid_set_member: %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); } if (target_name) { gss_buffer_desc name; name.value = target_name; name.length = strlen(target_name); maj_stat = gss_import_name(&min_stat, &name, GSS_C_NT_HOSTBASED_SERVICE, &target); if (maj_stat != GSS_S_COMPLETE) errx(1, "gss_import_name: %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); } for (i = 0; i < num_loops; i++) { cred = acquire_cred_service(acquire_name, type, oidset, flag); if (enctype) { int32_t enctypelist = enctype; maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, cred, 1, &enctypelist); if (maj_stat) errx(1, "gss_krb5_set_allowable_enctypes: %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); } if (target) { gss_ctx_id_t context = GSS_C_NO_CONTEXT; gss_buffer_desc out; out.length = 0; out.value = NULL; maj_stat = gss_init_sec_context(&min_stat, cred, &context, target, mechoid, GSS_C_MUTUAL_FLAG, 0, NULL, GSS_C_NO_BUFFER, NULL, &out, NULL, NULL); if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) errx(1, "init_sec_context failed: %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); gss_release_buffer(&min_stat, &out); gss_delete_sec_context(&min_stat, &context, NULL); } gss_release_cred(&min_stat, &cred); } return 0; }
static bool mag_auth_basic(request_rec *req, struct mag_config *cfg, gss_buffer_desc ba_user, gss_buffer_desc ba_pwd, gss_name_t *client, gss_OID *mech_type, gss_cred_id_t *delegated_cred, uint32_t *vtime) { #ifdef HAVE_GSS_KRB5_CCACHE_NAME const char *user_ccache = NULL; const char *orig_ccache = NULL; long long unsigned int rndname; apr_status_t rs; #endif gss_name_t user = GSS_C_NO_NAME; gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL; gss_ctx_id_t user_ctx = GSS_C_NO_CONTEXT; gss_name_t server = GSS_C_NO_NAME; gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL; gss_ctx_id_t server_ctx = GSS_C_NO_CONTEXT; gss_buffer_desc input = GSS_C_EMPTY_BUFFER; gss_buffer_desc output = GSS_C_EMPTY_BUFFER; gss_OID_set allowed_mechs; gss_OID_set filtered_mechs; gss_OID_set actual_mechs = GSS_C_NO_OID_SET; uint32_t init_flags = 0; uint32_t maj, min; int present = 0; bool ret = false; maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &user); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "In Basic Auth, %s", mag_error(req, "gss_import_name() failed", maj, min)); goto done; } if (cfg->basic_mechs) { allowed_mechs = cfg->basic_mechs; } else if (cfg->allowed_mechs) { allowed_mechs = cfg->allowed_mechs; } else { struct mag_server_config *scfg; /* Try to fetch the default set if not explicitly configured, * We need to do this because gss_acquire_cred_with_password() * is currently limited to acquire creds for a single "default" * mechanism if no desired mechanisms are passed in. This causes * authentication to fail for secondary mechanisms as no user * credentials are generated for those. */ scfg = ap_get_module_config(req->server->module_config, &auth_gssapi_module); /* In the worst case scenario default_mechs equals to GSS_C_NO_OID_SET. * This generally causes only the krb5 mechanism to be tried due * to implementation constraints, but may change in future. */ allowed_mechs = scfg->default_mechs; } /* Remove Spnego if present, or we'd repeat failed authentiations * multiple times, one within Spnego and then again with an explicit * mechanism. We would normally just force Spnego and use * gss_set_neg_mechs, but due to the way we source the server name * and the fact MIT up to 1.14 at least does no handle union names, * we can't provide spnego with a server name that can be used by * multiple mechanisms, causing any but the first mechanism to fail. * Also remove unwanted krb mechs, or AS requests will be repeated * multiple times uselessly. */ filtered_mechs = mag_filter_unwanted_mechs(allowed_mechs); if (filtered_mechs == allowed_mechs) { /* in case filtered_mechs was not allocated here don't free it */ filtered_mechs = GSS_C_NO_OID_SET; } else if (filtered_mechs == GSS_C_NO_OID_SET) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, req, "Fatal " "failure while filtering mechs, aborting"); goto done; } else { /* use the filtered list */ allowed_mechs = filtered_mechs; } #ifdef HAVE_GSS_KRB5_CCACHE_NAME /* If we are using the krb5 mechanism make sure to set a per thread * memory ccache so that there can't be interferences between threads. * Also make sure we have new cache so no cached results end up being * used. Some implementations of gss_acquire_cred_with_password() do * not reacquire creds if cached ones are around, failing to check * again for the password. */ maj = gss_test_oid_set_member(&min, discard_const(gss_mech_krb5), allowed_mechs, &present); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "In Basic Auth, %s", mag_error(req, "gss_test_oid_set_member() failed", maj, min)); goto done; } if (present) { rs = apr_generate_random_bytes((unsigned char *)(&rndname), sizeof(long long unsigned int)); if (rs != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "Failed to generate random ccache name"); goto done; } user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname); maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "In Basic Auth, %s", mag_error(req, "gss_krb5_ccache_name() " "failed", maj, min)); goto done; } } #endif maj = gss_acquire_cred_with_password(&min, user, &ba_pwd, GSS_C_INDEFINITE, allowed_mechs, GSS_C_INITIATE, &user_cred, &actual_mechs, NULL); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "In Basic Auth, %s", mag_error(req, "gss_acquire_cred_with_password() " "failed", maj, min)); goto done; } /* must acquire creds based on the actual mechs we want to try */ if (!mag_acquire_creds(req, cfg, actual_mechs, GSS_C_ACCEPT, &server_cred, NULL)) { goto done; } #ifdef HAVE_CRED_STORE if (cfg->deleg_ccache_dir) { /* delegate ourselves credentials so we store them as requested */ init_flags |= GSS_C_DELEG_FLAG; } #endif for (int i = 0; i < actual_mechs->count; i++) { /* free these if looping */ gss_release_buffer(&min, &output); gss_release_buffer(&min, &input); gss_release_name(&min, &server); maj = gss_inquire_cred_by_mech(&min, server_cred, &actual_mechs->elements[i], &server, NULL, NULL, NULL); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s", mag_error(req, "gss_inquired_cred_by_mech() " "failed", maj, min)); continue; } do { /* output and input are inverted here, this is intentional */ maj = gss_init_sec_context(&min, user_cred, &user_ctx, server, &actual_mechs->elements[i], init_flags, 300, GSS_C_NO_CHANNEL_BINDINGS, &output, NULL, &input, NULL, NULL); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s", mag_error(req, "gss_init_sec_context() " "failed", maj, min)); break; } gss_release_buffer(&min, &output); maj = gss_accept_sec_context(&min, &server_ctx, server_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)); break; } gss_release_buffer(&min, &input); } while (maj == GSS_S_CONTINUE_NEEDED); if (maj == GSS_S_COMPLETE) { ret = true; break; } } done: gss_release_buffer(&min, &output); gss_release_buffer(&min, &input); gss_release_name(&min, &server); gss_delete_sec_context(&min, &server_ctx, GSS_C_NO_BUFFER); gss_release_cred(&min, &server_cred); gss_release_name(&min, &user); gss_release_cred(&min, &user_cred); gss_delete_sec_context(&min, &user_ctx, GSS_C_NO_BUFFER); gss_release_oid_set(&min, &actual_mechs); gss_release_oid_set(&min, &filtered_mechs); #ifdef HAVE_GSS_KRB5_CCACHE_NAME if (user_ccache != NULL) { maj = gss_krb5_ccache_name(&min, orig_ccache, NULL); if (maj != GSS_S_COMPLETE) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "Failed to restore per-thread ccache, %s", mag_error(req, "gss_krb5_ccache_name() " "failed", maj, min)); } } #endif return ret; }