OM_uint32 GSSAPI_CALLCONV
_gss_krb5_acquire_cred_ext(OM_uint32 * minor_status,
			   const gss_name_t desired_name,
			   gss_const_OID credential_type,
			   const void *credential_data,
			   OM_uint32 time_req,
			   gss_const_OID desired_mech,
			   gss_cred_usage_t cred_usage,
			   gss_cred_id_t * output_cred_handle)
{
    krb5_init_creds_context ctx = NULL;
    krb5_get_init_creds_opt *opt = NULL;
    krb5_principal principal;
    krb5_context context;
    krb5_error_code kret;
    gsskrb5_cred handle = NULL;
    krb5_ccache ccache = NULL, ccachereplace = NULL;
    char *passwordstr = NULL;
    char *cache_name = NULL;
    char *lkdc_hostname = NULL;
    hx509_cert hxcert = NULL;
    heim_array_t bundleacl = NULL;
    krb5_principal new_name = NULL;

    GSSAPI_KRB5_INIT(&context);

    cred_usage &= GSS_C_OPTION_MASK;

    if (cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) {
	*minor_status = GSS_KRB5_S_G_BAD_USAGE;
	return GSS_S_FAILURE;
    }
    
    if (desired_name == GSS_C_NO_NAME)
	return GSS_S_FAILURE;

    if (gss_oid_equal(credential_type, GSS_C_CRED_HEIMBASE)) {
	heim_object_t pw, cname, cert, lkdc;
	heim_dict_t dict = (heim_dict_t)credential_data;

	pw = heim_dict_copy_value(dict, _gsskrb5_kGSSICPassword);
	if (pw) {
	    if (heim_get_tid(pw) == heim_string_get_type_id()) {
		passwordstr = heim_string_copy_utf8(pw);
		if (passwordstr == NULL) {
		    kret = ENOMEM;
		    goto out;
		}
	    } else if (heim_get_tid(pw) == heim_data_get_type_id()) {
		passwordstr = malloc(heim_data_get_length(pw) + 1);
		if (passwordstr == NULL) {
		    kret = ENOMEM;
		    goto out;
		}
		memcpy(passwordstr, heim_data_get_bytes(pw), heim_data_get_length(pw));
		passwordstr[heim_data_get_length(pw)] = '\0';
	    }
	    heim_release(pw);
	}

	cname = heim_dict_copy_value(dict, _gsskrb5_kGSSICKerberosCacheName);
	if (cname) {
	    cache_name = heim_string_copy_utf8(cname);
	    heim_release(cname);
	}
	
	bundleacl = heim_dict_copy_value(dict, _gsskrb5_kGSSICAppIdentifierACL);

#ifdef PKINIT
	cert = heim_dict_copy_value(dict, _gsskrb5_kGSSICCertificate);
	if (cert) {
	    kret = hx509_cert_init_SecFramework(context->hx509ctx, cert, &hxcert);
	    if (kret)
		goto out;
	    heim_release(cert);
	}
#endif

	lkdc = heim_dict_copy_value(dict, _gsskrb5_kGSSICLKDCHostname);
	if (lkdc) {
	    lkdc_hostname = heim_string_copy_utf8(lkdc);
	    heim_release(lkdc);
	}

    } else if (gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) {
	gss_buffer_t password = (gss_buffer_t)credential_data;
	
	passwordstr = malloc(password->length + 1);
	if (passwordstr == NULL) {
	    kret = ENOMEM;
	    goto out;
	}
	
	memcpy(passwordstr, password->value, password->length);
	passwordstr[password->length] = '\0';

    } else {
	*minor_status = KRB5_NOCREDS_SUPPLIED; /* XXX */
	return GSS_S_FAILURE;
    }

    if (passwordstr == NULL && hxcert == NULL) {
	*minor_status = KRB5_NOCREDS_SUPPLIED; /* XXX */
	return GSS_S_FAILURE;
    }

    *output_cred_handle = NULL;

    handle = calloc(1, sizeof(*handle));
    if (handle == NULL) {
	*minor_status = ENOMEM;
        return (GSS_S_FAILURE);
    }

    principal = (krb5_principal)desired_name;

    HEIMDAL_MUTEX_init(&handle->cred_id_mutex);

    kret = krb5_copy_principal(context, principal, &handle->principal);
    if (kret)
	goto out;

    kret = krb5_cc_new_unique(context, NULL, NULL, &ccache);
    if (kret)
	goto out;

    kret = krb5_get_init_creds_opt_alloc(context, &opt);
    if (kret)
	goto out;
    
    krb5_get_init_creds_opt_set_default_flags(context, "gss", krb5_principal_get_realm(context, principal), opt);

    krb5_get_init_creds_opt_set_forwardable(opt, 1);
    krb5_get_init_creds_opt_set_proxiable(opt, 1);
    krb5_get_init_creds_opt_set_renew_life(opt, 3600 * 24 * 30); /* 1 month */


    if (hxcert) {
	char *cert_pool[2] = { "KEYCHAIN:", NULL };
	kret = krb5_get_init_creds_opt_set_pkinit(context, opt, principal,
						 NULL, "KEYCHAIN:", 
						 cert_pool, NULL, 8,
						 NULL, NULL, NULL);
	if (kret)
	    goto out;
    }

    kret = krb5_init_creds_init(context, handle->principal, NULL, NULL, NULL, opt, &ctx);
    if (kret)
	goto out;

    if (passwordstr) {
	kret = krb5_init_creds_set_password(context, ctx, passwordstr);

	memset(passwordstr, 0, strlen(passwordstr));
	free(passwordstr);
	passwordstr = NULL;

	if (kret)
	    goto out;
    }

    if (hxcert) {
	kret = krb5_init_creds_set_pkinit_client_cert(context, ctx, hxcert);
	if (kret)
	    goto out;
    }

    if (lkdc_hostname) {
	kret = krb5_init_creds_set_kdc_hostname(context, ctx, lkdc_hostname);
	free(lkdc_hostname);
	lkdc_hostname = NULL;
	if (kret)
	    goto out;
    }

    kret = krb5_init_creds_get(context, ctx);
    if (kret)
	goto out;

    handle->endtime = _krb5_init_creds_get_cred_endtime(context, ctx);

    /*
     * If we where subjected to a referral, update the name of the credential
     */
    new_name = _krb5_init_creds_get_cred_client(context, ctx);
    if (new_name && !krb5_principal_compare(context, new_name, handle->principal)) {
	krb5_free_principal(context, handle->principal);
	kret = krb5_copy_principal(context, new_name, &handle->principal);
	if (kret)
	    goto out;
    }

    /*
     * Now store the credential
     */

    if (cache_name) {
	/* check if caller told us to use a specific cache */
	kret = krb5_cc_resolve(context, cache_name, &ccachereplace);
	if (kret)
	    goto out;

    } else {
	/*
	 * check if there an existing cache to overwrite before we lay
	 * down the new cache
	 */
	(void)krb5_cc_cache_match(context, principal, &ccachereplace);
    }


    kret = krb5_init_creds_store(context, ctx, ccache);
    if (kret == 0)
	kret = krb5_init_creds_store_config(context, ctx, ccache);

    if (bundleacl)
	krb5_cc_set_acl(context, ccache, "kHEIMAttrBundleIdentifierACL", bundleacl);

    krb5_init_creds_free(context, ctx);
    ctx = NULL;
    if (kret)
	goto out;

    krb5_get_init_creds_opt_free(context, opt);
    opt = NULL;

    /*
     * If we have a credential with the same naame, lets overwrite it
     */
    
    if (ccachereplace) {
	kret = krb5_cc_move(context, ccache, ccachereplace);
	if (kret)
	    goto out;
	handle->ccache = ccachereplace;
	ccachereplace = NULL;
    } else {
	handle->ccache = ccache;
    }

    handle->usage = cred_usage;
    *minor_status = 0;
    *output_cred_handle = (gss_cred_id_t)handle;

    if (cache_name)
	free(cache_name);

    heim_release(bundleacl);

    return GSS_S_COMPLETE;

 out:
    if (bundleacl)
	heim_release(bundleacl);
    if (opt)
	krb5_get_init_creds_opt_free(context, opt);
    if (ctx)
	krb5_init_creds_free(context, ctx);
    if (lkdc_hostname)
	free(lkdc_hostname);
    if (cache_name)
	free(cache_name);
    if (passwordstr) {
	memset(passwordstr, 0, strlen(passwordstr));
	free(passwordstr);
    }
    if (ccachereplace)
	krb5_cc_close(context, ccachereplace);
    if (ccache)
	krb5_cc_destroy(context, ccache);
    if (handle) {
	if (handle->principal)
	    krb5_free_principal(context, handle->principal);

	HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
	free(handle);
    }

    *minor_status = kret;
    return GSS_S_FAILURE;
}
Beispiel #2
0
static krb5_error_code
get_new_tickets(krb5_context context,
		krb5_principal principal,
		krb5_ccache ccache,
		krb5_deltat ticket_life,
		int interactive)
{
    krb5_error_code ret;
    krb5_get_init_creds_opt *opt;
    krb5_creds cred;
    char passwd[256];
    krb5_deltat start_time = 0;
    krb5_deltat renew = 0;
    const char *renewstr = NULL;
    krb5_enctype *enctype = NULL;
    krb5_ccache tempccache;
    krb5_init_creds_context icc;
    krb5_keytab kt = NULL;
    int will_use_keytab =  (use_keytab || keytab_str);
    krb5_prompter_fct prompter = NULL;
    int need_prompt;

    passwd[0] = '\0';

    if (password_file) {
	FILE *f;

	if (strcasecmp("STDIN", password_file) == 0)
	    f = stdin;
	else
	    f = fopen(password_file, "r");
	if (f == NULL)
	    krb5_errx(context, 1, "Failed to open the password file %s",
		      password_file);

	if (fgets(passwd, sizeof(passwd), f) == NULL)
	    krb5_errx(context, 1,
		      N_("Failed to read password from file %s", ""),
		      password_file);
	if (f != stdin)
	    fclose(f);
	passwd[strcspn(passwd, "\n")] = '\0';
    }

#if defined(__APPLE__) && !defined(__APPLE_TARGET_EMBEDDED__)
    if (passwd[0] == '\0' && !will_use_keytab && home_directory_flag) {
	const char *realm;
	OSStatus osret;
	UInt32 length;
	void *buffer;
	char *name;

	realm = krb5_principal_get_realm(context, principal);

	ret = krb5_unparse_name_flags(context, principal,
				      KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name);
	if (ret)
	    goto nopassword;

	osret = SecKeychainFindGenericPassword(NULL, (UInt32)strlen(realm), realm,
					       (UInt32)strlen(name), name,
					       &length, &buffer, &passwordItem);
	free(name);
	if (osret != noErr)
	    goto nopassword;

	if (length < sizeof(passwd) - 1) {
	    memcpy(passwd, buffer, length);
	    passwd[length] = '\0';
	}
	SecKeychainItemFreeContent(NULL, buffer);
    nopassword:
	do { } while(0);
    }
#endif

    need_prompt = !(pk_user_id || ent_user_id || anonymous_flag || will_use_keytab || passwd[0] != '\0') && interactive;
    if (need_prompt)
	prompter = krb5_prompter_posix;
    else
	prompter = krb5_prompter_print_only;

    memset(&cred, 0, sizeof(cred));

    ret = krb5_get_init_creds_opt_alloc (context, &opt);
    if (ret)
	krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc");

    krb5_get_init_creds_opt_set_default_flags(context, "kinit",
	krb5_principal_get_realm(context, principal), opt);

    if(forwardable_flag != -1)
	krb5_get_init_creds_opt_set_forwardable (opt, forwardable_flag);

    if(proxiable_flag != -1)
	krb5_get_init_creds_opt_set_proxiable (opt, proxiable_flag);
    if(anonymous_flag)
	krb5_get_init_creds_opt_set_anonymous (opt, anonymous_flag);
    if (pac_flag != -1)
	krb5_get_init_creds_opt_set_pac_request(context, opt,
						pac_flag ? TRUE : FALSE);
    if (canonicalize_flag)
	krb5_get_init_creds_opt_set_canonicalize(context, opt, TRUE);
    if (pk_enterprise_flag || enterprise_flag || canonicalize_flag || windows_flag)
	krb5_get_init_creds_opt_set_win2k(context, opt, TRUE);
    if (pk_user_id || ent_user_id || anonymous_flag) {
	ret = krb5_get_init_creds_opt_set_pkinit(context, opt,
						 principal,
						 pk_user_id,
						 pk_x509_anchors,
						 NULL,
						 NULL,
						 pk_use_enckey ? 2 : 0 |
						 anonymous_flag ? 4 : 0,
						 interactive ? krb5_prompter_posix : krb5_prompter_print_only,
						 NULL,
						 passwd);
	if (ret)
	    krb5_err(context, 1, ret, "krb5_get_init_creds_opt_set_pkinit");
	if (ent_user_id)
	    krb5_get_init_creds_opt_set_pkinit_user_cert(context, opt, ent_user_id);
    }

    if (addrs_flag != -1)
	krb5_get_init_creds_opt_set_addressless(context, opt,
						addrs_flag ? FALSE : TRUE);

    if (renew_life == NULL && renewable_flag)
	renewstr = "1 month";
    if (renew_life)
	renewstr = renew_life;
    if (renewstr) {
	renew = parse_time (renewstr, "s");
	if (renew < 0)
	    errx (1, "unparsable time: %s", renewstr);

	krb5_get_init_creds_opt_set_renew_life (opt, renew);
    }

    if(ticket_life != 0)
	krb5_get_init_creds_opt_set_tkt_life (opt, ticket_life);

    if(start_str) {
	int tmp = parse_time (start_str, "s");
	if (tmp < 0)
	    errx (1, N_("unparsable time: %s", ""), start_str);

	start_time = tmp;
    }

    if(etype_str.num_strings) {
	int i;

	enctype = malloc(etype_str.num_strings * sizeof(*enctype));
	if(enctype == NULL)
	    errx(1, "out of memory");
	for(i = 0; i < etype_str.num_strings; i++) {
	    ret = krb5_string_to_enctype(context,
					 etype_str.strings[i],
					 &enctype[i]);
	    if(ret)
		krb5_err(context, 1, ret, "unrecognized enctype: %s",
			 etype_str.strings[i]);
	}
	krb5_get_init_creds_opt_set_etype_list(opt, enctype,
					       etype_str.num_strings);
    }

    ret = krb5_init_creds_init(context, principal,
			       prompter, NULL,
			       start_time, opt, &icc);
    if (ret)
	krb5_err (context, 1, ret, "krb5_init_creds_init");

    if (server_str) {
	ret = krb5_init_creds_set_service(context, icc, server_str);
	if (ret)
	    krb5_err (context, 1, ret, "krb5_init_creds_set_service");
    }

    if (kdc_hostname)
	krb5_init_creds_set_kdc_hostname(context, icc, kdc_hostname);

    if (fast_armor_cache_string) {
	krb5_ccache fastid;
	
	ret = krb5_cc_resolve(context, fast_armor_cache_string, &fastid);
	if (ret)
	    krb5_err(context, 1, ret, "krb5_cc_resolve(FAST cache)");
	
	ret = krb5_init_creds_set_fast_ccache(context, icc, fastid);
	if (ret)
	    krb5_err(context, 1, ret, "krb5_init_creds_set_fast_ccache");
    }

    if(will_use_keytab) {
	if(keytab_str)
	    ret = krb5_kt_resolve(context, keytab_str, &kt);
	else
	    ret = krb5_kt_default(context, &kt);
	if (ret)
	    krb5_err (context, 1, ret, "resolving keytab");

	ret = krb5_init_creds_set_keytab(context, icc, kt);
	if (ret)
	    krb5_err (context, 1, ret, "krb5_init_creds_set_keytab");
    }

    if (passwd[0] == '\0' && need_prompt) {
	char *p, *prompt;

	krb5_unparse_name(context, principal, &p);
	asprintf (&prompt, N_("%s's Password: "******""), p);
	free(p);

	if (UI_UTIL_read_pw_string(passwd, sizeof(passwd)-1, prompt, 0)){
	    memset(passwd, 0, sizeof(passwd));
	    errx(1, "failed to read password");
	}
	free (prompt);
    }

    if (passwd[0]) {
	ret = krb5_init_creds_set_password(context, icc, passwd);
	if (ret)
	    krb5_err(context, 1, ret, "krb5_init_creds_set_password");
    }

    ret = krb5_init_creds_get(context, icc);

#ifdef __APPLE__
    /*
     * Save password in Keychain
     */
    if (ret == 0 && keychain_flag && passwordItem == NULL) {
	krb5_error_code ret2;
	const char *realm;
	char *name;

	realm = krb5_principal_get_realm(context, principal);
	ret2 = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name);
	if (ret2 == 0) {
	    (void)SecKeychainAddGenericPassword(NULL,
						(UInt32)strlen(realm), realm,
						(UInt32)strlen(name), name,
						(UInt32)strlen(passwd), passwd,
						NULL);
	    free(name);
	}
    }
#endif

    memset(passwd, 0, sizeof(passwd));

    switch(ret){
    case 0:
	break;
    case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */
	exit(1);
    case KRB5KRB_AP_ERR_BAD_INTEGRITY:
    case KRB5KRB_AP_ERR_MODIFIED:
    case KRB5KDC_ERR_PREAUTH_FAILED:
    case KRB5_GET_IN_TKT_LOOP:
#ifdef __APPLE__
	if (passwordItem)
	    SecKeychainItemDelete(passwordItem);
#endif
	krb5_errx(context, 1, N_("Password incorrect", ""));
    case KRB5KRB_AP_ERR_V4_REPLY:
	krb5_errx(context, 1, N_("Looks like a Kerberos 4 reply", ""));
    case KRB5KDC_ERR_KEY_EXPIRED:
	krb5_errx(context, 1, N_("Password expired", ""));
    default:
	krb5_err(context, 1, ret, "krb5_get_init_creds");
    }

    ret = krb5_init_creds_get_creds(context, icc, &cred);
    if (ret)
	krb5_err(context, 1, ret, "krb5_init_creds_get_creds");

    krb5_process_last_request(context, opt, icc);

    ret = krb5_cc_new_unique(context, krb5_cc_get_type(context, ccache),
			     NULL, &tempccache);
    if (ret)
	krb5_err (context, 1, ret, "krb5_cc_new_unique");

    ret = krb5_init_creds_store(context, icc, tempccache);
    if (ret)
	krb5_err(context, 1, ret, "krb5_init_creds_store");

    ret = krb5_init_creds_store_config(context, icc, tempccache);
    if (ret)
	krb5_warn(context, ret, "krb5_init_creds_store_config");

    ret = krb5_init_creds_warn_user(context, icc);
    if (ret)
	krb5_warn(context, ret, "krb5_init_creds_warn_user");

#ifdef __APPLE__
    /*
     * Set for this case, default to * so that all processes can use
     * this cache.
     */
    {
	heim_array_t bundleacl = heim_array_create();
	heim_string_t ace;

	if (bundle_acl_strings.num_strings > 0) {
	    int i;
	    for (i = 0; i < bundle_acl_strings.num_strings; i++) {
		ace = heim_string_create(bundle_acl_strings.strings[i]);
		heim_array_append_value(bundleacl, ace);
		heim_release(ace);
	    }
	} else {
	    ace = heim_string_create("*");
	    heim_array_append_value(bundleacl, ace);
	    heim_release(ace);
	}
	krb5_cc_set_acl(context, tempccache, "kHEIMAttrBundleIdentifierACL", bundleacl);
	heim_release(bundleacl);
    }
#endif

    ret = krb5_cc_move(context, tempccache, ccache);
    if (ret) {
	(void)krb5_cc_destroy(context, tempccache);
	krb5_err (context, 1, ret, "krb5_cc_move");
    }

    if (switch_cache_flags)
	krb5_cc_switch(context, ccache);

    if (ok_as_delegate_flag || windows_flag || use_referrals_flag) {
	unsigned char d = 0;
	krb5_data data;

	if (ok_as_delegate_flag || windows_flag)
	    d |= 1;
	if (use_referrals_flag || windows_flag)
	    d |= 2;

	data.length = 1;
	data.data = &d;

	krb5_cc_set_config(context, ccache, NULL, "realm-config", &data);
    }

    if (enctype)
	free(enctype);

    krb5_init_creds_free(context, icc);
    krb5_get_init_creds_opt_free(context, opt);

    if (kt)
	krb5_kt_close(context, kt);

#ifdef __APPLE__
    if (passwordItem)
	CFRelease(passwordItem);
#endif

    return 0;
}