8187443: Forest Consolidation: Move files to unified layout

Reviewed-by: darcy, ihse
This commit is contained in:
Erik Joelsson 2017-09-12 19:03:39 +02:00
parent 270fe13182
commit 3789983e89
56923 changed files with 3 additions and 15727 deletions

View file

@ -0,0 +1,222 @@
/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#import <Cocoa/Cocoa.h>
#import <JavaNativeFoundation/JavaNativeFoundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
@interface JNFVectorCoercion : NSObject <JNFTypeCoercion> { }
@end
@implementation JNFVectorCoercion
- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer {
static JNF_CLASS_CACHE(jc_Vector, "java/util/Vector");
static JNF_CTOR_CACHE(jm_Vector_ctor, jc_Vector, "(I)V");
static JNF_MEMBER_CACHE(jm_Vector_add, jc_Vector, "add", "(Ljava/lang/Object;)Z");
NSArray *nsArray = (NSArray *)obj;
jobject javaArray = JNFNewObject(env, jm_Vector_ctor, (jint)[nsArray count]);
for (id obj in nsArray) {
jobject jobj = [coercer coerceNSObject:obj withEnv:env usingCoercer:coercer];
JNFCallBooleanMethod(env, javaArray, jm_Vector_add, jobj);
if (jobj != NULL) (*env)->DeleteLocalRef(env, jobj);
}
return javaArray;
}
- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer {
return nil;
}
@end
@interface JNFHashtableCoercion : NSObject <JNFTypeCoercion> { }
@end
@implementation JNFHashtableCoercion
- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer {
static JNF_CLASS_CACHE(jc_Hashtable, "java/util/Hashtable");
static JNF_CTOR_CACHE(jm_Hashtable_ctor, jc_Hashtable, "()V");
static JNF_MEMBER_CACHE(jm_Hashtable_put, jc_Hashtable, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
NSDictionary *nsDict = (NSDictionary *)obj;
NSEnumerator *keyEnum = [nsDict keyEnumerator];
jobject jHashTable = JNFNewObject(env, jm_Hashtable_ctor);
id key = nil;
while ((key = [keyEnum nextObject]) != nil) {
jobject jkey = [coercer coerceNSObject:key withEnv:env usingCoercer:coercer];
id value = [nsDict objectForKey:key];
jobject jvalue = [coercer coerceNSObject:value withEnv:env usingCoercer:coercer];
JNFCallObjectMethod(env, jHashTable, jm_Hashtable_put, jkey, jvalue);
if (jkey != NULL) (*env)->DeleteLocalRef(env, jkey);
if (jvalue != NULL) (*env)->DeleteLocalRef(env, jvalue);
}
return jHashTable;
}
- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer {
return nil;
}
@end
NSDictionary *realmConfigsForRealms(SCDynamicStoreRef store, NSArray *realms) {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for (NSString *realm in realms) {
CFTypeRef realmInfo = SCDynamicStoreCopyValue(store, (CFStringRef) [NSString stringWithFormat:@"Kerberos:%@", realm]);
if (CFGetTypeID(realmInfo) != CFDictionaryGetTypeID()) {
return nil;
}
[dict setObject:(NSArray *)realmInfo forKey:realm];
CFRelease(realmInfo);
}
return dict;
}
#define KERBEROS_DEFAULT_REALMS @"Kerberos-Default-Realms"
#define KERBEROS_DEFAULT_REALM_MAPPINGS @"Kerberos-Domain-Realm-Mappings"
void _SCDynamicStoreCallBack(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info) {
NSArray *keys = (NSArray *)changedKeys;
if ([keys count] == 0) return;
if (![keys containsObject:KERBEROS_DEFAULT_REALMS] && ![keys containsObject:KERBEROS_DEFAULT_REALM_MAPPINGS]) return;
JNFPerformEnvBlock(JNFThreadDetachOnThreadDeath | JNFThreadSetSystemClassLoaderOnAttach | JNFThreadAttachAsDaemon, ^(JNIEnv *env) {
static JNF_CLASS_CACHE(jc_Config, "sun/security/krb5/Config");
static JNF_STATIC_MEMBER_CACHE(jm_Config_refresh, jc_Config, "refresh", "()V");
JNFCallStaticVoidMethod(env, jm_Config_refresh);
});
}
/*
* Class: sun_security_krb5_SCDynamicStoreConfig
* Method: installNotificationCallback
*/
JNIEXPORT void JNICALL Java_sun_security_krb5_SCDynamicStoreConfig_installNotificationCallback(JNIEnv *env, jclass klass) {
JNF_COCOA_ENTER(env);
SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("java"), _SCDynamicStoreCallBack, NULL);
if (store == NULL) {
return;
}
NSArray *keys = [NSArray arrayWithObjects:KERBEROS_DEFAULT_REALMS, KERBEROS_DEFAULT_REALM_MAPPINGS, nil];
SCDynamicStoreSetNotificationKeys(store, (CFArrayRef) keys, NULL);
CFRunLoopSourceRef rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
if (rls != NULL) {
CFRunLoopAddSource(CFRunLoopGetMain(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
}
CFRelease(store);
JNF_COCOA_EXIT(env);
}
/*
* Class: sun_security_krb5_SCDynamicStoreConfig
* Method: getKerberosConfig
* Signature: ()Ljava/util/Hashtable;
*/
JNIEXPORT jobject JNICALL Java_sun_security_krb5_SCDynamicStoreConfig_getKerberosConfig(JNIEnv *env, jclass klass) {
jobject jHashTable = NULL;
JNF_COCOA_ENTER(env);
SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("java-kerberos"), NULL, NULL);
if (store == NULL) {
return NULL;
}
CFTypeRef realms = SCDynamicStoreCopyValue(store, (CFStringRef) KERBEROS_DEFAULT_REALMS);
if (realms == NULL || CFGetTypeID(realms) != CFArrayGetTypeID()) {
if (realms) CFRelease(realms);
CFRelease(store);
return NULL;
}
CFTypeRef realmMappings = SCDynamicStoreCopyValue(store, (CFStringRef) KERBEROS_DEFAULT_REALM_MAPPINGS);
if (realmMappings == NULL || CFGetTypeID(realmMappings) != CFArrayGetTypeID()) {
if (realmMappings) CFRelease(realmMappings);
CFRelease(realms);
CFRelease(store);
return NULL;
}
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
if (CFArrayGetCount(realms) > 0) {
NSDictionary *defaultRealmsDict = [NSDictionary dictionaryWithObject:[(NSArray *)realms objectAtIndex:0] forKey:@"default_realm"];
[dict setObject:defaultRealmsDict forKey:@"libdefaults"];
NSDictionary *realmConfigs = realmConfigsForRealms(store, (NSArray *)realms);
[dict setObject:realmConfigs forKey:@"realms"];
}
CFRelease(realms);
CFRelease(store);
if (CFArrayGetCount(realmMappings) > 0) {
[dict setObject:[(NSArray *)realmMappings objectAtIndex:0] forKey:@"domain_realm"];
}
CFRelease(realmMappings);
// create and load a coercer with all of the different coercions to convert each type of object
JNFTypeCoercer *coercer = [[[JNFTypeCoercer alloc] init] autorelease];
[JNFDefaultCoercions addStringCoercionTo:coercer];
[JNFDefaultCoercions addNumberCoercionTo:coercer];
[coercer addCoercion:[[[JNFHashtableCoercion alloc] init] autorelease] forNSClass:[NSDictionary class] javaClass:@"java/util/Map"];
[coercer addCoercion:[[[JNFVectorCoercion alloc] init] autorelease] forNSClass:[NSArray class] javaClass:@"java/util/List"];
// convert Cocoa graph to Java graph
jHashTable = [coercer coerceNSObject:dict withEnv:env];
JNF_COCOA_EXIT(env);
return jHashTable;
}

View file

@ -0,0 +1,599 @@
/*
* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#import "sun_security_krb5_Credentials.h"
#import <Kerberos/Kerberos.h>
#import <string.h>
#import <time.h>
#include "jni_util.h"
/*
* Based largely on klist.c,
*
* Created by Scott Kovatch on 8/12/04.
*
* See http://www.opensource.apple.com/darwinsource/10.3.3/Kerberos-47/KerberosClients/klist/Sources/klist.c
*/
/*
* Statics for this module
*/
static jclass derValueClass = NULL;
static jclass ticketClass = NULL;
static jclass principalNameClass = NULL;
static jclass encryptionKeyClass = NULL;
static jclass ticketFlagsClass = NULL;
static jclass kerberosTimeClass = NULL;
static jclass javaLangStringClass = NULL;
static jclass javaLangIntegerClass = NULL;
static jclass hostAddressClass = NULL;
static jclass hostAddressesClass = NULL;
static jmethodID derValueConstructor = 0;
static jmethodID ticketConstructor = 0;
static jmethodID principalNameConstructor = 0;
static jmethodID encryptionKeyConstructor = 0;
static jmethodID ticketFlagsConstructor = 0;
static jmethodID kerberosTimeConstructor = 0;
static jmethodID krbcredsConstructor = 0;
static jmethodID integerConstructor = 0;
static jmethodID hostAddressConstructor = 0;
static jmethodID hostAddressesConstructor = 0;
/*
* Function prototypes for internal routines
*/
static jobject BuildTicket(JNIEnv *env, krb5_data *encodedTicket);
static jobject BuildClientPrincipal(JNIEnv *env, krb5_context kcontext, krb5_principal principalName);
static jobject BuildEncryptionKey(JNIEnv *env, krb5_keyblock *cryptoKey);
static jobject BuildTicketFlags(JNIEnv *env, krb5_flags flags);
static jobject BuildKerberosTime(JNIEnv *env, krb5_timestamp kerbtime);
static jobject BuildAddressList(JNIEnv *env, krb5_address **kerbtime);
static void printiferr (errcode_t err, const char *format, ...);
static jclass FindClass(JNIEnv *env, char *className)
{
jclass cls = (*env)->FindClass(env, className);
if (cls == NULL) {
printf("Couldn't find %s\n", className);
return NULL;
}
jobject returnValue = (*env)->NewWeakGlobalRef(env,cls);
return returnValue;
}
/*
* Class: sun_security_krb5_KrbCreds
* Method: JNI_OnLoad
*/
JNIEXPORT jint JNICALL DEF_JNI_OnLoad(JavaVM *jvm, void *reserved)
{
JNIEnv *env;
if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
return JNI_EVERSION; /* JNI version not supported */
}
ticketClass = FindClass(env, "sun/security/krb5/internal/Ticket");
if (ticketClass == NULL) return JNI_ERR;
principalNameClass = FindClass(env, "sun/security/krb5/PrincipalName");
if (principalNameClass == NULL) return JNI_ERR;
derValueClass = FindClass(env, "sun/security/util/DerValue");
if (derValueClass == NULL) return JNI_ERR;
encryptionKeyClass = FindClass(env, "sun/security/krb5/EncryptionKey");
if (encryptionKeyClass == NULL) return JNI_ERR;
ticketFlagsClass = FindClass(env,"sun/security/krb5/internal/TicketFlags");
if (ticketFlagsClass == NULL) return JNI_ERR;
kerberosTimeClass = FindClass(env,"sun/security/krb5/internal/KerberosTime");
if (kerberosTimeClass == NULL) return JNI_ERR;
javaLangStringClass = FindClass(env,"java/lang/String");
if (javaLangStringClass == NULL) return JNI_ERR;
javaLangIntegerClass = FindClass(env,"java/lang/Integer");
if (javaLangIntegerClass == NULL) return JNI_ERR;
hostAddressClass = FindClass(env,"sun/security/krb5/internal/HostAddress");
if (hostAddressClass == NULL) return JNI_ERR;
hostAddressesClass = FindClass(env,"sun/security/krb5/internal/HostAddresses");
if (hostAddressesClass == NULL) return JNI_ERR;
derValueConstructor = (*env)->GetMethodID(env, derValueClass, "<init>", "([B)V");
if (derValueConstructor == 0) {
printf("Couldn't find DerValue constructor\n");
return JNI_ERR;
}
ticketConstructor = (*env)->GetMethodID(env, ticketClass, "<init>", "(Lsun/security/util/DerValue;)V");
if (ticketConstructor == 0) {
printf("Couldn't find Ticket constructor\n");
return JNI_ERR;
}
principalNameConstructor = (*env)->GetMethodID(env, principalNameClass, "<init>", "(Ljava/lang/String;I)V");
if (principalNameConstructor == 0) {
printf("Couldn't find PrincipalName constructor\n");
return JNI_ERR;
}
encryptionKeyConstructor = (*env)->GetMethodID(env, encryptionKeyClass, "<init>", "(I[B)V");
if (encryptionKeyConstructor == 0) {
printf("Couldn't find EncryptionKey constructor\n");
return JNI_ERR;
}
ticketFlagsConstructor = (*env)->GetMethodID(env, ticketFlagsClass, "<init>", "(I[B)V");
if (ticketFlagsConstructor == 0) {
printf("Couldn't find TicketFlags constructor\n");
return JNI_ERR;
}
kerberosTimeConstructor = (*env)->GetMethodID(env, kerberosTimeClass, "<init>", "(J)V");
if (kerberosTimeConstructor == 0) {
printf("Couldn't find KerberosTime constructor\n");
return JNI_ERR;
}
integerConstructor = (*env)->GetMethodID(env, javaLangIntegerClass, "<init>", "(I)V");
if (integerConstructor == 0) {
printf("Couldn't find Integer constructor\n");
return JNI_ERR;
}
hostAddressConstructor = (*env)->GetMethodID(env, hostAddressClass, "<init>", "(I[B)V");
if (hostAddressConstructor == 0) {
printf("Couldn't find HostAddress constructor\n");
return JNI_ERR;
}
hostAddressesConstructor = (*env)->GetMethodID(env, hostAddressesClass, "<init>", "([Lsun/security/krb5/internal/HostAddress;)V");
if (hostAddressesConstructor == 0) {
printf("Couldn't find HostAddresses constructor\n");
return JNI_ERR;
}
return JNI_VERSION_1_2;
}
/*
* Class: sun_security_jgss_KrbCreds
* Method: JNI_OnUnload
*/
JNIEXPORT void JNICALL DEF_JNI_OnUnload(JavaVM *jvm, void *reserved)
{
JNIEnv *env;
if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_2)) {
return; /* Nothing else we can do */
}
if (ticketClass != NULL) {
(*env)->DeleteWeakGlobalRef(env,ticketClass);
}
if (derValueClass != NULL) {
(*env)->DeleteWeakGlobalRef(env,derValueClass);
}
if (principalNameClass != NULL) {
(*env)->DeleteWeakGlobalRef(env,principalNameClass);
}
if (encryptionKeyClass != NULL) {
(*env)->DeleteWeakGlobalRef(env,encryptionKeyClass);
}
if (ticketFlagsClass != NULL) {
(*env)->DeleteWeakGlobalRef(env,ticketFlagsClass);
}
if (kerberosTimeClass != NULL) {
(*env)->DeleteWeakGlobalRef(env,kerberosTimeClass);
}
if (javaLangStringClass != NULL) {
(*env)->DeleteWeakGlobalRef(env,javaLangStringClass);
}
if (javaLangIntegerClass != NULL) {
(*env)->DeleteWeakGlobalRef(env,javaLangIntegerClass);
}
if (hostAddressClass != NULL) {
(*env)->DeleteWeakGlobalRef(env,hostAddressClass);
}
if (hostAddressesClass != NULL) {
(*env)->DeleteWeakGlobalRef(env,hostAddressesClass);
}
}
int isIn(krb5_enctype e, int n, jint* etypes)
{
int i;
for (i=0; i<n; i++) {
if (e == etypes[i]) return 1;
}
return 0;
}
/*
* Class: sun_security_krb5_Credentials
* Method: acquireDefaultNativeCreds
* Signature: ([I])Lsun/security/krb5/Credentials;
*/
JNIEXPORT jobject JNICALL Java_sun_security_krb5_Credentials_acquireDefaultNativeCreds
(JNIEnv *env, jclass krbcredsClass, jintArray jetypes)
{
jobject krbCreds = NULL;
krb5_error_code err = 0;
krb5_ccache ccache = NULL;
krb5_cc_cursor cursor = NULL;
krb5_creds creds;
krb5_flags flags = 0;
krb5_context kcontext = NULL;
int netypes;
jint *etypes = NULL;
/* Initialize the Kerberos 5 context */
err = krb5_init_context (&kcontext);
if (!err) {
err = krb5_cc_default (kcontext, &ccache);
}
if (!err) {
err = krb5_cc_set_flags (kcontext, ccache, flags); /* turn off OPENCLOSE */
}
if (!err) {
err = krb5_cc_start_seq_get (kcontext, ccache, &cursor);
}
netypes = (*env)->GetArrayLength(env, jetypes);
etypes = (jint *) (*env)->GetIntArrayElements(env, jetypes, NULL);
if (etypes != NULL && !err) {
while ((err = krb5_cc_next_cred (kcontext, ccache, &cursor, &creds)) == 0) {
char *serverName = NULL;
if (!err) {
err = krb5_unparse_name (kcontext, creds.server, &serverName);
printiferr (err, "while unparsing server name");
}
if (!err) {
char* slash = strchr(serverName, '/');
char* at = strchr(serverName, '@');
// Make sure the server's name is krbtgt/REALM@REALM, the etype
// is supported, and the ticket has not expired
if (slash && at &&
strncmp (serverName, "krbtgt", slash-serverName) == 0 &&
// the ablove line shows at must be after slash
strncmp (slash+1, at+1, at-slash-1) == 0 &&
isIn (creds.keyblock.enctype, netypes, etypes) &&
creds.times.endtime > time(0)) {
jobject ticket, clientPrincipal, targetPrincipal, encryptionKey;
jobject ticketFlags, startTime, endTime;
jobject authTime, renewTillTime, hostAddresses;
ticket = clientPrincipal = targetPrincipal = encryptionKey = NULL;
ticketFlags = startTime = endTime = NULL;
authTime = renewTillTime = hostAddresses = NULL;
// For the default credentials we're only interested in the krbtgt server.
clientPrincipal = BuildClientPrincipal(env, kcontext, creds.client);
if (clientPrincipal == NULL) goto cleanup;
targetPrincipal = BuildClientPrincipal(env, kcontext, creds.server);
if (targetPrincipal == NULL) goto cleanup;
// Build a sun/security/krb5/internal/Ticket
ticket = BuildTicket(env, &creds.ticket);
if (ticket == NULL) goto cleanup;
// Get the encryption key
encryptionKey = BuildEncryptionKey(env, &creds.keyblock);
if (encryptionKey == NULL) goto cleanup;
// and the ticket flags
ticketFlags = BuildTicketFlags(env, creds.ticket_flags);
if (ticketFlags == NULL) goto cleanup;
// Get the timestamps out.
startTime = BuildKerberosTime(env, creds.times.starttime);
if (startTime == NULL) goto cleanup;
authTime = BuildKerberosTime(env, creds.times.authtime);
if (authTime == NULL) goto cleanup;
endTime = BuildKerberosTime(env, creds.times.endtime);
if (endTime == NULL) goto cleanup;
renewTillTime = BuildKerberosTime(env, creds.times.renew_till);
if (renewTillTime == NULL) goto cleanup;
// Create the addresses object.
hostAddresses = BuildAddressList(env, creds.addresses);
if (krbcredsConstructor == 0) {
krbcredsConstructor = (*env)->GetMethodID(env, krbcredsClass, "<init>",
"(Lsun/security/krb5/internal/Ticket;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/EncryptionKey;Lsun/security/krb5/internal/TicketFlags;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/HostAddresses;)V");
if (krbcredsConstructor == 0) {
printf("Couldn't find sun.security.krb5.internal.Ticket constructor\n");
break;
}
}
// and now go build a KrbCreds object
krbCreds = (*env)->NewObject(
env,
krbcredsClass,
krbcredsConstructor,
ticket,
clientPrincipal,
targetPrincipal,
encryptionKey,
ticketFlags,
authTime,
startTime,
endTime,
renewTillTime,
hostAddresses);
cleanup:
if (ticket) (*env)->DeleteLocalRef(env, ticket);
if (clientPrincipal) (*env)->DeleteLocalRef(env, clientPrincipal);
if (targetPrincipal) (*env)->DeleteLocalRef(env, targetPrincipal);
if (encryptionKey) (*env)->DeleteLocalRef(env, encryptionKey);
if (ticketFlags) (*env)->DeleteLocalRef(env, ticketFlags);
if (authTime) (*env)->DeleteLocalRef(env, authTime);
if (startTime) (*env)->DeleteLocalRef(env, startTime);
if (endTime) (*env)->DeleteLocalRef(env, endTime);
if (renewTillTime) (*env)->DeleteLocalRef(env, renewTillTime);
if (hostAddresses) (*env)->DeleteLocalRef(env, hostAddresses);
// Stop if there is an exception or we already found the initial TGT
if ((*env)->ExceptionCheck(env) || krbCreds) {
break;
}
}
}
if (serverName != NULL) { krb5_free_unparsed_name (kcontext, serverName); }
krb5_free_cred_contents (kcontext, &creds);
}
if (err == KRB5_CC_END) { err = 0; }
printiferr (err, "while retrieving a ticket");
}
if (!err) {
err = krb5_cc_end_seq_get (kcontext, ccache, &cursor);
printiferr (err, "while finishing ticket retrieval");
}
if (!err) {
flags = KRB5_TC_OPENCLOSE; /* restore OPENCLOSE mode */
err = krb5_cc_set_flags (kcontext, ccache, flags);
printiferr (err, "while finishing ticket retrieval");
}
if (etypes != NULL) {
(*env)->ReleaseIntArrayElements(env, jetypes, etypes, 0);
}
krb5_free_context (kcontext);
return krbCreds;
}
#pragma mark -
jobject BuildTicket(JNIEnv *env, krb5_data *encodedTicket)
{
/* To build a Ticket, we first need to build a DerValue out of the EncodedTicket.
* But before we can do that, we need to make a byte array out of the ET.
*/
jobject derValue, ticket;
jbyteArray ary;
ary = (*env)->NewByteArray(env, encodedTicket->length);
if ((*env)->ExceptionCheck(env)) {
return (jobject) NULL;
}
(*env)->SetByteArrayRegion(env, ary, (jsize) 0, encodedTicket->length, (jbyte *)encodedTicket->data);
if ((*env)->ExceptionCheck(env)) {
(*env)->DeleteLocalRef(env, ary);
return (jobject) NULL;
}
derValue = (*env)->NewObject(env, derValueClass, derValueConstructor, ary);
if ((*env)->ExceptionCheck(env)) {
(*env)->DeleteLocalRef(env, ary);
return (jobject) NULL;
}
(*env)->DeleteLocalRef(env, ary);
ticket = (*env)->NewObject(env, ticketClass, ticketConstructor, derValue);
if ((*env)->ExceptionCheck(env)) {
(*env)->DeleteLocalRef(env, derValue);
return (jobject) NULL;
}
(*env)->DeleteLocalRef(env, derValue);
return ticket;
}
jobject BuildClientPrincipal(JNIEnv *env, krb5_context kcontext, krb5_principal principalName) {
// Get the full principal string.
char *principalString = NULL;
jobject principal = NULL;
int err = krb5_unparse_name (kcontext, principalName, &principalString);
if (!err) {
// Make a PrincipalName from the full string and the type. Let the PrincipalName class parse it out.
jstring principalStringObj = (*env)->NewStringUTF(env, principalString);
if (principalStringObj == NULL) {
if (principalString != NULL) { krb5_free_unparsed_name (kcontext, principalString); }
return (jobject) NULL;
}
principal = (*env)->NewObject(env, principalNameClass, principalNameConstructor, principalStringObj, principalName->type);
if (principalString != NULL) { krb5_free_unparsed_name (kcontext, principalString); }
(*env)->DeleteLocalRef(env, principalStringObj);
}
return principal;
}
jobject BuildEncryptionKey(JNIEnv *env, krb5_keyblock *cryptoKey) {
// First, need to build a byte array
jbyteArray ary;
jobject encryptionKey = NULL;
ary = (*env)->NewByteArray(env,cryptoKey->length);
if (ary == NULL) {
return (jobject) NULL;
}
(*env)->SetByteArrayRegion(env, ary, (jsize) 0, cryptoKey->length, (jbyte *)cryptoKey->contents);
if (!(*env)->ExceptionCheck(env)) {
encryptionKey = (*env)->NewObject(env, encryptionKeyClass, encryptionKeyConstructor, cryptoKey->enctype, ary);
}
(*env)->DeleteLocalRef(env, ary);
return encryptionKey;
}
jobject BuildTicketFlags(JNIEnv *env, krb5_flags flags) {
jobject ticketFlags = NULL;
jbyteArray ary;
/*
* Convert the bytes to network byte order before copying
* them to a Java byte array.
*/
unsigned long nlflags = htonl(flags);
ary = (*env)->NewByteArray(env, sizeof(flags));
if (ary == NULL) {
return (jobject) NULL;
}
(*env)->SetByteArrayRegion(env, ary, (jsize) 0, sizeof(flags), (jbyte *)&nlflags);
if (!(*env)->ExceptionCheck(env)) {
ticketFlags = (*env)->NewObject(env, ticketFlagsClass, ticketFlagsConstructor, sizeof(flags)*8, ary);
}
(*env)->DeleteLocalRef(env, ary);
return ticketFlags;
}
jobject BuildKerberosTime(JNIEnv *env, krb5_timestamp kerbtime) {
jlong time = kerbtime;
// Kerberos time is in seconds, but the KerberosTime class assumes milliseconds, so multiply by 1000.
time *= 1000;
return (*env)->NewObject(env, kerberosTimeClass, kerberosTimeConstructor, time);
}
jobject BuildAddressList(JNIEnv *env, krb5_address **addresses) {
if (addresses == NULL) {
return NULL;
}
int addressCount = 0;
// See how many we have.
krb5_address **p = addresses;
while (*p != 0) {
addressCount++;
p++;
}
jobject address_list = (*env)->NewObjectArray(env, addressCount, hostAddressClass, NULL);
if (address_list == NULL) {
return (jobject) NULL;
}
// Create a new HostAddress object for each address block.
// First, reset the iterator.
p = addresses;
jsize index = 0;
while (*p != 0) {
krb5_address *currAddress = *p;
// HostAddres needs a byte array of the host data.
jbyteArray ary = (*env)->NewByteArray(env, currAddress->length);
if (ary == NULL) return NULL;
(*env)->SetByteArrayRegion(env, ary, (jsize) 0, currAddress->length, (jbyte *)currAddress->contents);
jobject address = (*env)->NewObject(env, hostAddressClass, hostAddressConstructor, currAddress->length, ary);
(*env)->DeleteLocalRef(env, ary);
if (address == NULL) {
return (jobject) NULL;
}
// Add the HostAddress to the arrray.
(*env)->SetObjectArrayElement(env, address_list, index, address);
if ((*env)->ExceptionCheck(env)) {
return (jobject) NULL;
}
index++;
p++;
}
return address_list;
}
#pragma mark - Utility methods -
static void printiferr (errcode_t err, const char *format, ...)
{
if (err) {
va_list pvar;
va_start (pvar, format);
com_err_va ("ticketParser:", err, format, pvar);
va_end (pvar);
}
}

View file

@ -0,0 +1,339 @@
/*
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.security.auth.kerberos;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.security.BasicPermission;
import java.security.Permission;
import java.security.PermissionCollection;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* This class is used to restrict the usage of the Kerberos
* delegation model, ie: forwardable and proxiable tickets.
* <p>
* The target name of this {@code Permission} specifies a pair of
* kerberos service principals. The first is the subordinate service principal
* being entrusted to use the TGT. The second service principal designates
* the target service the subordinate service principal is to
* interact with on behalf of the initiating KerberosPrincipal. This
* latter service principal is specified to restrict the use of a
* proxiable ticket.
* <p>
* For example, to specify the "host" service use of a forwardable TGT the
* target permission is specified as follows:
*
* <pre>
* DelegationPermission("\"host/foo.example.com@EXAMPLE.COM\" \"krbtgt/EXAMPLE.COM@EXAMPLE.COM\"");
* </pre>
* <p>
* To give the "backup" service a proxiable nfs service ticket the target permission
* might be specified:
*
* <pre>
* DelegationPermission("\"backup/bar.example.com@EXAMPLE.COM\" \"nfs/home.EXAMPLE.COM@EXAMPLE.COM\"");
* </pre>
*
* @since 1.4
*/
public final class DelegationPermission extends BasicPermission
implements java.io.Serializable {
private static final long serialVersionUID = 883133252142523922L;
private transient String subordinate, service;
/**
* Create a new {@code DelegationPermission}
* with the specified subordinate and target principals.
*
* @param principals the name of the subordinate and target principals
*
* @throws NullPointerException if {@code principals} is {@code null}.
* @throws IllegalArgumentException if {@code principals} is empty.
*/
public DelegationPermission(String principals) {
super(principals);
init(principals);
}
/**
* Create a new {@code DelegationPermission}
* with the specified subordinate and target principals.
*
* @param principals the name of the subordinate and target principals
*
* @param actions should be null.
*
* @throws NullPointerException if {@code principals} is {@code null}.
* @throws IllegalArgumentException if {@code principals} is empty.
*/
public DelegationPermission(String principals, String actions) {
super(principals, actions);
init(principals);
}
/**
* Initialize the DelegationPermission object.
*/
private void init(String target) {
StringTokenizer t = null;
if (!target.startsWith("\"")) {
throw new IllegalArgumentException
("service principal [" + target +
"] syntax invalid: " +
"improperly quoted");
} else {
t = new StringTokenizer(target, "\"", false);
subordinate = t.nextToken();
if (t.countTokens() == 2) {
t.nextToken(); // bypass whitespace
service = t.nextToken();
} else if (t.countTokens() > 0) {
throw new IllegalArgumentException
("service principal [" + t.nextToken() +
"] syntax invalid: " +
"improperly quoted");
}
}
}
/**
* Checks if this Kerberos delegation permission object "implies" the
* specified permission.
* <P>
* This method returns true if this {@code DelegationPermission}
* is equal to {@code p}, and returns false otherwise.
*
* @param p the permission to check against.
*
* @return true if the specified permission is implied by this object,
* false if not.
*/
@Override
public boolean implies(Permission p) {
return equals(p);
}
/**
* Checks two DelegationPermission objects for equality.
*
* @param obj the object to test for equality with this object.
*
* @return true if {@code obj} is a DelegationPermission, and
* has the same subordinate and service principal as this
* DelegationPermission object.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof DelegationPermission)) {
return false;
}
DelegationPermission that = (DelegationPermission) obj;
return this.subordinate.equals(that.subordinate) &&
this.service.equals(that.service);
}
/**
* Returns the hash code value for this object.
*
* @return a hash code value for this object.
*/
@Override
public int hashCode() {
return 17 * subordinate.hashCode() + 31 * service.hashCode();
}
/**
* Returns a PermissionCollection object for storing
* DelegationPermission objects.
* <br>
* DelegationPermission objects must be stored in a manner that
* allows them to be inserted into the collection in any order, but
* that also enables the PermissionCollection implies method to
* be implemented in an efficient (and consistent) manner.
*
* @return a new PermissionCollection object suitable for storing
* DelegationPermissions.
*/
@Override
public PermissionCollection newPermissionCollection() {
return new KrbDelegationPermissionCollection();
}
/**
* WriteObject is called to save the state of the DelegationPermission
* to a stream. The actions are serialized, and the superclass
* takes care of the name.
*/
private synchronized void writeObject(java.io.ObjectOutputStream s)
throws IOException
{
s.defaultWriteObject();
}
/**
* readObject is called to restore the state of the
* DelegationPermission from a stream.
*/
private synchronized void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
// Read in the action, then initialize the rest
s.defaultReadObject();
init(getName());
}
}
final class KrbDelegationPermissionCollection extends PermissionCollection
implements java.io.Serializable {
// Not serialized; see serialization section at end of class.
private transient ConcurrentHashMap<Permission, Boolean> perms;
public KrbDelegationPermissionCollection() {
perms = new ConcurrentHashMap<>();
}
/**
* Check and see if this collection of permissions implies the permissions
* expressed in "permission".
*
* @param permission the Permission object to compare
*
* @return true if "permission" is a proper subset of a permission in
* the collection, false if not.
*/
@Override
public boolean implies(Permission permission) {
if (! (permission instanceof DelegationPermission))
return false;
// if map contains key, then it automatically implies it
return perms.containsKey(permission);
}
/**
* Adds a permission to the DelegationPermissions. The key for
* the hash is the name.
*
* @param permission the Permission object to add.
*
* @exception IllegalArgumentException - if the permission is not a
* DelegationPermission
*
* @exception SecurityException - if this PermissionCollection object
* has been marked readonly
*/
@Override
public void add(Permission permission) {
if (! (permission instanceof DelegationPermission))
throw new IllegalArgumentException("invalid permission: "+
permission);
if (isReadOnly())
throw new SecurityException("attempt to add a Permission to a readonly PermissionCollection");
perms.put(permission, Boolean.TRUE);
}
/**
* Returns an enumeration of all the DelegationPermission objects
* in the container.
*
* @return an enumeration of all the DelegationPermission objects.
*/
@Override
public Enumeration<Permission> elements() {
return perms.keys();
}
private static final long serialVersionUID = -3383936936589966948L;
// Need to maintain serialization interoperability with earlier releases,
// which had the serializable field:
// private Vector permissions;
/**
* @serialField permissions java.util.Vector
* A list of DelegationPermission objects.
*/
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("permissions", Vector.class),
};
/**
* @serialData "permissions" field (a Vector containing the DelegationPermissions).
*/
/*
* Writes the contents of the perms field out as a Vector for
* serialization compatibility with earlier releases.
*/
private void writeObject(ObjectOutputStream out) throws IOException {
// Don't call out.defaultWriteObject()
// Write out Vector
Vector<Permission> permissions = new Vector<>(perms.keySet());
ObjectOutputStream.PutField pfields = out.putFields();
pfields.put("permissions", permissions);
out.writeFields();
}
/*
* Reads in a Vector of DelegationPermissions and saves them in the perms field.
*/
@SuppressWarnings("unchecked")
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException
{
// Don't call defaultReadObject()
// Read in serialized fields
ObjectInputStream.GetField gfields = in.readFields();
// Get the one we want
Vector<Permission> permissions =
(Vector<Permission>)gfields.get("permissions", null);
perms = new ConcurrentHashMap<>(permissions.size());
for (Permission perm : permissions) {
perms.put(perm, Boolean.TRUE);
}
}
}

View file

@ -0,0 +1,220 @@
/*
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.security.auth.kerberos;
import java.util.Arrays;
import java.util.Objects;
import javax.crypto.SecretKey;
import javax.security.auth.DestroyFailedException;
/**
* This class encapsulates an EncryptionKey used in Kerberos.<p>
*
* An EncryptionKey is defined in Section 4.2.9 of the Kerberos Protocol
* Specification (<a href=http://www.ietf.org/rfc/rfc4120.txt>RFC 4120</a>) as:
* <pre>
* EncryptionKey ::= SEQUENCE {
* keytype [0] Int32 -- actually encryption type --,
* keyvalue [1] OCTET STRING
* }
* </pre>
* The key material of an {@code EncryptionKey} is defined as the value
* of the {@code keyValue} above.
*
* @since 9
*/
public final class EncryptionKey implements SecretKey {
private static final long serialVersionUID = 9L;
/**
* {@code KeyImpl} is serialized by writing out the ASN.1 encoded bytes
* of the encryption key.
*
* @serial
*/
final private KeyImpl key;
private transient boolean destroyed = false;
/**
* Constructs an {@code EncryptionKey} from the given bytes and
* the key type.
* <p>
* The contents of the byte array are copied; subsequent modification of
* the byte array does not affect the newly created key.
*
* @param keyBytes the key material for the key
* @param keyType the key type for the key as defined by the
* Kerberos protocol specification.
* @throws NullPointerException if keyBytes is null
*/
public EncryptionKey(byte[] keyBytes, int keyType) {
key = new KeyImpl(Objects.requireNonNull(keyBytes), keyType);
}
/**
* Returns the key type for this key.
*
* @return the key type.
* @throws IllegalStateException if the key is destroyed
*/
public int getKeyType() {
// KeyImpl already checked if destroyed
return key.getKeyType();
}
/*
* Methods from java.security.Key
*/
/**
* Returns the standard algorithm name for this key. The algorithm names
* are the encryption type string defined on the IANA
* <a href="https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-1">Kerberos Encryption Type Numbers</a>
* page.
* <p>
* This method can return the following value not defined on the IANA page:
* <ol>
* <li>none: for etype equal to 0</li>
* <li>unknown: for etype greater than 0 but unsupported by
* the implementation</li>
* <li>private: for etype smaller than 0</li>
* </ol>
*
* @return the name of the algorithm associated with this key.
* @throws IllegalStateException if the key is destroyed
*/
@Override
public String getAlgorithm() {
// KeyImpl already checked if destroyed
return key.getAlgorithm();
}
/**
* Returns the name of the encoding format for this key.
*
* @return the String "RAW"
* @throws IllegalStateException if the key is destroyed
*/
@Override
public String getFormat() {
// KeyImpl already checked if destroyed
return key.getFormat();
}
/**
* Returns the key material of this key.
*
* @return a newly allocated byte array that contains the key material
* @throws IllegalStateException if the key is destroyed
*/
@Override
public byte[] getEncoded() {
// KeyImpl already checked if destroyed
return key.getEncoded();
}
/**
* Destroys this key by clearing out the key material of this key.
*
* @throws DestroyFailedException if some error occurs while destorying
* this key.
*/
@Override
public void destroy() throws DestroyFailedException {
if (!destroyed) {
key.destroy();
destroyed = true;
}
}
@Override
public boolean isDestroyed() {
return destroyed;
}
/**
* Returns an informative textual representation of this {@code EncryptionKey}.
*
* @return an informative textual representation of this {@code EncryptionKey}.
*/
@Override
public String toString() {
if (destroyed) {
return "Destroyed EncryptionKey";
}
return "key " + key.toString();
}
/**
* Returns a hash code for this {@code EncryptionKey}.
*
* @return a hash code for this {@code EncryptionKey}.
*/
@Override
public int hashCode() {
int result = 17;
if (isDestroyed()) {
return result;
}
result = 37 * result + Arrays.hashCode(getEncoded());
return 37 * result + getKeyType();
}
/**
* Compares the specified object with this key for equality.
* Returns true if the given object is also an
* {@code EncryptionKey} and the two
* {@code EncryptionKey} instances are equivalent. More formally two
* {@code EncryptionKey} instances are equal if they have equal key types
* and key material.
* A destroyed {@code EncryptionKey} object is only equal to itself.
*
* @param other the object to compare to
* @return true if the specified object is equal to this
* {@code EncryptionKey}, false otherwise.
*/
@Override
public boolean equals(Object other) {
if (other == this)
return true;
if (! (other instanceof EncryptionKey)) {
return false;
}
EncryptionKey otherKey = ((EncryptionKey) other);
if (isDestroyed() || otherKey.isDestroyed()) {
return false;
}
return getKeyType() == otherKey.getKeyType()
&& Arrays.equals(getEncoded(), otherKey.getEncoded());
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.security.auth.kerberos;
import sun.security.krb5.JavaxSecurityAuthKerberosAccess;
import sun.security.krb5.EncryptionKey;
import sun.security.krb5.PrincipalName;
class JavaxSecurityAuthKerberosAccessImpl
implements JavaxSecurityAuthKerberosAccess {
public sun.security.krb5.internal.ktab.KeyTab keyTabTakeSnapshot(
KeyTab ktab) {
return ktab.takeSnapshot();
}
}

View file

@ -0,0 +1,194 @@
/*
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.security.auth.kerberos;
import javax.security.auth.Destroyable;
import java.util.Arrays;
import java.util.Base64;
import java.util.Objects;
/**
* This class encapsulates a Kerberos 5 KRB_CRED message which can be used to
* send Kerberos credentials from one principal to another.<p>
*
* A KRB_CRED message is defined in Section 5.8.1 of the Kerberos Protocol
* Specification (<a href=http://www.ietf.org/rfc/rfc4120.txt>RFC 4120</a>) as:
* <pre>
* KRB-CRED ::= [APPLICATION 22] SEQUENCE {
* pvno [0] INTEGER (5),
* msg-type [1] INTEGER (22),
* tickets [2] SEQUENCE OF Ticket,
* enc-part [3] EncryptedData -- EncKrbCredPart
* }
* </pre>
*
* @since 9
*/
public final class KerberosCredMessage implements Destroyable {
final private KerberosPrincipal sender;
final private KerberosPrincipal recipient;
final private byte[] message;
private boolean destroyed = false;
/**
* Constructs a {@code KerberosCredMessage} object.
* <p>
* The contents of the {@code message} argument are copied; subsequent
* modification of the byte array does not affect the newly created object.
*
* @param sender the sender of the message
* @param recipient the recipient of the message
* @param message the DER encoded KRB_CRED message
* @throws NullPointerException if any of sender, recipient
* or message is null
*/
public KerberosCredMessage(KerberosPrincipal sender,
KerberosPrincipal recipient,
byte[] message) {
this.sender = Objects.requireNonNull(sender);
this.recipient = Objects.requireNonNull(recipient);
this.message = Objects.requireNonNull(message).clone();
}
/**
* Returns the DER encoded form of the KRB_CRED message.
*
* @return a newly allocated byte array that contains the encoded form
* @throws IllegalStateException if the object is destroyed
*/
public byte[] getEncoded() {
if (destroyed) {
throw new IllegalStateException("This object is no longer valid");
}
return message.clone();
}
/**
* Returns the sender of this message.
*
* @return the sender
* @throws IllegalStateException if the object is destroyed
*/
public KerberosPrincipal getSender() {
if (destroyed) {
throw new IllegalStateException("This object is no longer valid");
}
return sender;
}
/**
* Returns the recipient of this message.
*
* @return the recipient
* @throws IllegalStateException if the object is destroyed
*/
public KerberosPrincipal getRecipient() {
if (destroyed) {
throw new IllegalStateException("This object is no longer valid");
}
return recipient;
}
/**
* Destroys this object by clearing out the message.
*/
@Override
public void destroy() {
if (!destroyed) {
Arrays.fill(message, (byte)0);
destroyed = true;
}
}
@Override
public boolean isDestroyed() {
return destroyed;
}
/**
* Returns an informative textual representation of this {@code KerberosCredMessage}.
*
* @return an informative textual representation of this {@code KerberosCredMessage}.
*/
@Override
public String toString() {
if (destroyed) {
return "Destroyed KerberosCredMessage";
} else {
return "KRB_CRED from " + sender + " to " + recipient + ":\n"
+ Base64.getUrlEncoder().encodeToString(message);
}
}
/**
* Returns a hash code for this {@code KerberosCredMessage}.
*
* @return a hash code for this {@code KerberosCredMessage}.
*/
@Override
public int hashCode() {
if (isDestroyed()) {
return -1;
} else {
return Objects.hash(sender, recipient, Arrays.hashCode(message));
}
}
/**
* Compares the specified object with this {@code KerberosCredMessage}
* for equality. Returns true if the given object is also a
* {@code KerberosCredMessage} and the two {@code KerberosCredMessage}
* instances are equivalent. More formally two {@code KerberosCredMessage}
* instances are equal if they have equal sender, recipient, and encoded
* KRB_CRED messages.
* A destroyed {@code KerberosCredMessage} object is only equal to itself.
*
* @param other the object to compare to
* @return true if the specified object is equal to this
* {@code KerberosCredMessage}, false otherwise.
*/
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (! (other instanceof KerberosCredMessage)) {
return false;
}
KerberosCredMessage otherMessage = ((KerberosCredMessage) other);
if (isDestroyed() || otherMessage.isDestroyed()) {
return false;
}
return Objects.equals(sender, otherMessage.sender)
&& Objects.equals(recipient, otherMessage.recipient)
&& Arrays.equals(message, otherMessage.message);
}
}

View file

@ -0,0 +1,342 @@
/*
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.security.auth.kerberos;
import java.util.Arrays;
import javax.crypto.SecretKey;
import javax.security.auth.DestroyFailedException;
/**
* This class encapsulates a long term secret key for a Kerberos
* principal.<p>
*
* A {@code KerberosKey} object includes an EncryptionKey, a
* {@link KerberosPrincipal} as its owner, and the version number
* of the key.<p>
*
* An EncryptionKey is defined in Section 4.2.9 of the Kerberos Protocol
* Specification (<a href=http://www.ietf.org/rfc/rfc4120.txt>RFC 4120</a>) as:
* <pre>
* EncryptionKey ::= SEQUENCE {
* keytype [0] Int32 -- actually encryption type --,
* keyvalue [1] OCTET STRING
* }
* </pre>
* The key material of a {@code KerberosKey} is defined as the value
* of the {@code keyValue} above.<p>
*
* All Kerberos JAAS login modules that obtain a principal's password and
* generate the secret key from it should use this class.
* Sometimes, such as when authenticating a server in
* the absence of user-to-user authentication, the login module will store
* an instance of this class in the private credential set of a
* {@link javax.security.auth.Subject Subject} during the commit phase of the
* authentication process.<p>
*
* A Kerberos service using a keytab to read secret keys should use
* the {@link KeyTab} class, where latest keys can be read when needed.<p>
*
* It might be necessary for the application to be granted a
* {@link javax.security.auth.PrivateCredentialPermission
* PrivateCredentialPermission} if it needs to access the {@code KerberosKey}
* instance from a Subject. This permission is not needed when the
* application depends on the default JGSS Kerberos mechanism to access the
* {@code KerberosKey}. In that case, however, the application will need an
* appropriate
* {@link javax.security.auth.kerberos.ServicePermission ServicePermission}.<p>
*
* When creating a {@code KerberosKey} using the
* {@link #KerberosKey(KerberosPrincipal, char[], String)} constructor,
* an implementation may accept non-IANA algorithm names (For example,
* "ArcFourMac" for "rc4-hmac"), but the {@link #getAlgorithm} method
* must always return the IANA algorithm name.
*
* @implNote Old algorithm names used before JDK 9 are supported in the
* {@link #KerberosKey(KerberosPrincipal, char[], String)} constructor in this
* implementation for compatibility reasons, which are "DES" (and null) for
* "des-cbc-md5", "DESede" for "des3-cbc-sha1-kd", "ArcFourHmac" for "rc4-hmac",
* "AES128" for "aes128-cts-hmac-sha1-96", and "AES256" for
* "aes256-cts-hmac-sha1-96".
*
* @author Mayank Upadhyay
* @since 1.4
*/
public class KerberosKey implements SecretKey {
private static final long serialVersionUID = -4625402278148246993L;
/**
* The principal that this secret key belongs to.
*
* @serial
*/
private KerberosPrincipal principal;
/**
* the version number of this secret key
*
* @serial
*/
private final int versionNum;
/**
* {@code KeyImpl} is serialized by writing out the ASN.1 encoded bytes
* of the encryption key.
*
* @serial
*/
private KeyImpl key;
private transient boolean destroyed = false;
/**
* Constructs a {@code KerberosKey} from the given bytes when the key type
* and key version number are known. This can be used when reading the
* secret key information from a Kerberos "keytab".
*
* @param principal the principal that this secret key belongs to
* @param keyBytes the key material for the secret key
* @param keyType the key type for the secret key as defined by the
* Kerberos protocol specification.
* @param versionNum the version number of this secret key
*/
public KerberosKey(KerberosPrincipal principal,
byte[] keyBytes,
int keyType,
int versionNum) {
this.principal = principal;
this.versionNum = versionNum;
key = new KeyImpl(keyBytes, keyType);
}
/**
* Constructs a {@code KerberosKey} from a principal's password using the
* specified algorithm name. The algorithm name (case insensitive) should
* be provided as the encryption type string defined on the IANA
* <a href="https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-1">Kerberos Encryption Type Numbers</a>
* page. The version number of the key generated will be 0.
*
* @param principal the principal that this password belongs to
* @param password the password that should be used to compute the key
* @param algorithm the name for the algorithm that this key will be
* used for
* @throws IllegalArgumentException if the name of the
* algorithm passed is unsupported.
*/
public KerberosKey(KerberosPrincipal principal,
char[] password,
String algorithm) {
this.principal = principal;
this.versionNum = 0;
// Pass principal in for salt
key = new KeyImpl(principal, password, algorithm);
}
/**
* Returns the principal that this key belongs to.
*
* @return the principal this key belongs to.
* @throws IllegalStateException if the key is destroyed
*/
public final KerberosPrincipal getPrincipal() {
if (destroyed) {
throw new IllegalStateException("This key is no longer valid");
}
return principal;
}
/**
* Returns the key version number.
*
* @return the key version number.
* @throws IllegalStateException if the key is destroyed
*/
public final int getVersionNumber() {
if (destroyed) {
throw new IllegalStateException("This key is no longer valid");
}
return versionNum;
}
/**
* Returns the key type for this long-term key.
*
* @return the key type.
* @throws IllegalStateException if the key is destroyed
*/
public final int getKeyType() {
// KeyImpl already checked if destroyed
return key.getKeyType();
}
/*
* Methods from java.security.Key
*/
/**
* Returns the standard algorithm name for this key. The algorithm names
* are the encryption type string defined on the IANA
* <a href="https://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml#kerberos-parameters-1">Kerberos Encryption Type Numbers</a>
* page.
* <p>
* This method can return the following value not defined on the IANA page:
* <ol>
* <li>none: for etype equal to 0</li>
* <li>unknown: for etype greater than 0 but unsupported by
* the implementation</li>
* <li>private: for etype smaller than 0</li>
* </ol>
*
* @return the name of the algorithm associated with this key.
* @throws IllegalStateException if the key is destroyed
*/
public final String getAlgorithm() {
// KeyImpl already checked if destroyed
return key.getAlgorithm();
}
/**
* Returns the name of the encoding format for this secret key.
*
* @return the String "RAW"
* @throws IllegalStateException if the key is destroyed
*/
public final String getFormat() {
// KeyImpl already checked if destroyed
return key.getFormat();
}
/**
* Returns the key material of this secret key.
*
* @return the key material
* @throws IllegalStateException if the key is destroyed
*/
public final byte[] getEncoded() {
// KeyImpl already checked if destroyed
return key.getEncoded();
}
/**
* Destroys this key by clearing out the key material of this secret key.
*
* @throws DestroyFailedException if some error occurs while destorying
* this key.
*/
public void destroy() throws DestroyFailedException {
if (!destroyed) {
key.destroy();
principal = null;
destroyed = true;
}
}
/** Determines if this key has been destroyed.*/
public boolean isDestroyed() {
return destroyed;
}
/**
* Returns an informative textual representation of this {@code KerberosKey}.
*
* @return an informative textual representation of this {@code KerberosKey}.
*/
public String toString() {
if (destroyed) {
return "Destroyed KerberosKey";
}
return "Kerberos Principal " + principal +
"Key Version " + versionNum +
"key " + key.toString();
}
/**
* Returns a hash code for this {@code KerberosKey}.
*
* @return a hash code for this {@code KerberosKey}.
* @since 1.6
*/
public int hashCode() {
int result = 17;
if (isDestroyed()) {
return result;
}
result = 37 * result + Arrays.hashCode(getEncoded());
result = 37 * result + getKeyType();
if (principal != null) {
result = 37 * result + principal.hashCode();
}
return result * 37 + versionNum;
}
/**
* Compares the specified object with this {@code KerberosKey} for
* equality. Returns true if the given object is also a
* {@code KerberosKey} and the two
* {@code KerberosKey} instances are equivalent.
* A destroyed {@code KerberosKey} object is only equal to itself.
*
* @param other the object to compare to
* @return true if the specified object is equal to this {@code KerberosKey},
* false otherwise.
* @since 1.6
*/
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (! (other instanceof KerberosKey)) {
return false;
}
KerberosKey otherKey = ((KerberosKey) other);
if (isDestroyed() || otherKey.isDestroyed()) {
return false;
}
if (versionNum != otherKey.getVersionNumber() ||
getKeyType() != otherKey.getKeyType() ||
!Arrays.equals(getEncoded(), otherKey.getEncoded())) {
return false;
}
if (principal == null) {
if (otherKey.getPrincipal() != null) {
return false;
}
} else {
if (!principal.equals(otherKey.getPrincipal())) {
return false;
}
}
return true;
}
}

View file

@ -0,0 +1,310 @@
/*
* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.security.auth.kerberos;
import java.io.*;
import sun.security.krb5.KrbException;
import sun.security.krb5.PrincipalName;
import sun.security.krb5.Realm;
import sun.security.util.*;
/**
* This class encapsulates a Kerberos principal.
*
* @author Mayank Upadhyay
* @since 1.4
*/
public final class KerberosPrincipal
implements java.security.Principal, java.io.Serializable {
private static final long serialVersionUID = -7374788026156829911L;
//name types
/**
* unknown name type.
*/
public static final int KRB_NT_UNKNOWN = 0;
/**
* user principal name type.
*/
public static final int KRB_NT_PRINCIPAL = 1;
/**
* service and other unique instance (krbtgt) name type.
*/
public static final int KRB_NT_SRV_INST = 2;
/**
* service with host name as instance (telnet, rcommands) name type.
*/
public static final int KRB_NT_SRV_HST = 3;
/**
* service with host as remaining components name type.
*/
public static final int KRB_NT_SRV_XHST = 4;
/**
* unique ID name type.
*/
public static final int KRB_NT_UID = 5;
private transient String fullName;
private transient String realm;
private transient int nameType;
/**
* Constructs a {@code KerberosPrincipal} from the provided string input.
* The name type for this principal defaults to
* {@link #KRB_NT_PRINCIPAL KRB_NT_PRINCIPAL}
* This string is assumed to contain a name in the format
* that is specified in Section 2.1.1. (Kerberos Principal Name Form) of
* <a href=http://www.ietf.org/rfc/rfc1964.txt> RFC 1964 </a>
* (for example, <i>duke@FOO.COM</i>, where <i>duke</i>
* represents a principal, and <i>FOO.COM</i> represents a realm).
*
* <p>If the input name does not contain a realm, the default realm
* is used. The default realm can be specified either in a Kerberos
* configuration file or via the java.security.krb5.realm
* system property. For more information, see the
* {@extLink security_guide_jgss_tutorial Kerberos Requirements}.
* Additionally, if a security manager is
* installed, a {@link ServicePermission} must be granted and the service
* principal of the permission must minimally be inside the
* {@code KerberosPrincipal}'s realm. For example, if the result of
* {@code new KerberosPrincipal("user")} is {@code user@EXAMPLE.COM},
* then a {@code ServicePermission} with service principal
* {@code host/www.example.com@EXAMPLE.COM} (and any action)
* must be granted.
*
* @param name the principal name
* @throws IllegalArgumentException if name is improperly
* formatted, if name is null, or if name does not contain
* the realm to use and the default realm is not specified
* in either a Kerberos configuration file or via the
* java.security.krb5.realm system property.
* @throws SecurityException if a security manager is installed and
* {@code name} does not contain the realm to use, and a proper
* {@link ServicePermission} as described above is not granted.
*/
public KerberosPrincipal(String name) {
this(name, KRB_NT_PRINCIPAL);
}
/**
* Constructs a {@code KerberosPrincipal} from the provided string and
* name type input. The string is assumed to contain a name in the
* format that is specified in Section 2.1 (Mandatory Name Forms) of
* <a href=http://www.ietf.org/rfc/rfc1964.txt>RFC 1964</a>.
* Valid name types are specified in Section 6.2 (Principal Names) of
* <a href=http://www.ietf.org/rfc/rfc4120.txt>RFC 4120</a>.
* The input name must be consistent with the provided name type.
* (for example, <i>duke@FOO.COM</i>, is a valid input string for the
* name type, KRB_NT_PRINCIPAL where <i>duke</i>
* represents a principal, and <i>FOO.COM</i> represents a realm).
*
* <p>If the input name does not contain a realm, the default realm
* is used. The default realm can be specified either in a Kerberos
* configuration file or via the java.security.krb5.realm
* system property. For more information, see the
* {@extLink security_guide_jgss_tutorial Kerberos Requirements}.
* Additionally, if a security manager is
* installed, a {@link ServicePermission} must be granted and the service
* principal of the permission must minimally be inside the
* {@code KerberosPrincipal}'s realm. For example, if the result of
* {@code new KerberosPrincipal("user")} is {@code user@EXAMPLE.COM},
* then a {@code ServicePermission} with service principal
* {@code host/www.example.com@EXAMPLE.COM} (and any action)
* must be granted.
*
* @param name the principal name
* @param nameType the name type of the principal
* @throws IllegalArgumentException if name is improperly
* formatted, if name is null, if the nameType is not supported,
* or if name does not contain the realm to use and the default
* realm is not specified in either a Kerberos configuration
* file or via the java.security.krb5.realm system property.
* @throws SecurityException if a security manager is installed and
* {@code name} does not contain the realm to use, and a proper
* {@link ServicePermission} as described above is not granted.
*/
public KerberosPrincipal(String name, int nameType) {
PrincipalName krb5Principal = null;
try {
// Appends the default realm if it is missing
krb5Principal = new PrincipalName(name,nameType);
} catch (KrbException e) {
throw new IllegalArgumentException(e.getMessage());
}
if (krb5Principal.isRealmDeduced() && !Realm.AUTODEDUCEREALM) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
sm.checkPermission(new ServicePermission(
"@" + krb5Principal.getRealmAsString(), "-"));
} catch (SecurityException se) {
// Swallow the actual exception to hide info
throw new SecurityException("Cannot read realm info");
}
}
}
this.nameType = nameType;
fullName = krb5Principal.toString();
realm = krb5Principal.getRealmString();
}
/**
* Returns the realm component of this Kerberos principal.
*
* @return the realm component of this Kerberos principal.
*/
public String getRealm() {
return realm;
}
/**
* Returns a hash code for this {@code KerberosPrincipal}. The hash code
* is defined to be the result of the following calculation:
* <pre>{@code
* hashCode = getName().hashCode();
* }</pre>
*
* @return a hash code for this {@code KerberosPrincipal}.
*/
public int hashCode() {
return getName().hashCode();
}
/**
* Compares the specified object with this principal for equality.
* Returns true if the given object is also a
* {@code KerberosPrincipal} and the two
* {@code KerberosPrincipal} instances are equivalent.
* More formally two {@code KerberosPrincipal} instances are equal
* if the values returned by {@code getName()} are equal.
*
* @param other the object to compare to
* @return true if the object passed in represents the same principal
* as this one, false otherwise.
*/
public boolean equals(Object other) {
if (other == this)
return true;
if (! (other instanceof KerberosPrincipal)) {
return false;
}
String myFullName = getName();
String otherFullName = ((KerberosPrincipal) other).getName();
return myFullName.equals(otherFullName);
}
/**
* Save the {@code KerberosPrincipal} object to a stream
*
* @serialData this {@code KerberosPrincipal} is serialized
* by writing out the PrincipalName and the
* Realm in their DER-encoded form as specified in Section 5.2.2 of
* <a href=http://www.ietf.org/rfc/rfc4120.txt> RFC4120</a>.
*/
private void writeObject(ObjectOutputStream oos)
throws IOException {
PrincipalName krb5Principal;
try {
krb5Principal = new PrincipalName(fullName, nameType);
oos.writeObject(krb5Principal.asn1Encode());
oos.writeObject(krb5Principal.getRealm().asn1Encode());
} catch (Exception e) {
throw new IOException(e);
}
}
/**
* Reads this object from a stream (i.e., deserializes it)
*/
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
byte[] asn1EncPrincipal = (byte [])ois.readObject();
byte[] encRealm = (byte [])ois.readObject();
try {
Realm realmObject = new Realm(new DerValue(encRealm));
PrincipalName krb5Principal = new PrincipalName(
new DerValue(asn1EncPrincipal), realmObject);
realm = realmObject.toString();
fullName = krb5Principal.toString();
nameType = krb5Principal.getNameType();
} catch (Exception e) {
throw new IOException(e);
}
}
/**
* The returned string corresponds to the single-string
* representation of a Kerberos Principal name as specified in
* Section 2.1 of <a href=http://www.ietf.org/rfc/rfc1964.txt>RFC 1964</a>.
*
* @return the principal name.
*/
public String getName() {
return fullName;
}
/**
* Returns the name type of the {@code KerberosPrincipal}. Valid name types
* are specified in Section 6.2 of
* <a href=http://www.ietf.org/rfc/rfc4120.txt> RFC4120</a>.
*
* @return the name type.
*/
public int getNameType() {
return nameType;
}
/**
* Returns an informative textual representation of this {@code KerberosPrincipal}.
*
* @return an informative textual representation of this {@code KerberosPrincipal}.
*/
public String toString() {
return getName();
}
}

View file

@ -0,0 +1,833 @@
/*
* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.security.auth.kerberos;
import java.io.*;
import java.util.Date;
import java.util.Arrays;
import java.net.InetAddress;
import javax.crypto.SecretKey;
import javax.security.auth.Refreshable;
import javax.security.auth.Destroyable;
import javax.security.auth.RefreshFailedException;
import javax.security.auth.DestroyFailedException;
import sun.security.util.HexDumpEncoder;
/**
* This class encapsulates a Kerberos ticket and associated
* information as viewed from the client's point of view. It captures all
* information that the Key Distribution Center (KDC) sends to the client
* in the reply message KDC-REP defined in the Kerberos Protocol
* Specification (<a href=http://www.ietf.org/rfc/rfc4120.txt>RFC 4120</a>).
* <p>
* All Kerberos JAAS login modules that authenticate a user to a KDC should
* use this class. Where available, the login module might even read this
* information from a ticket cache in the operating system instead of
* directly communicating with the KDC. During the commit phase of the JAAS
* authentication process, the JAAS login module should instantiate this
* class and store the instance in the private credential set of a
* {@link javax.security.auth.Subject Subject}.<p>
*
* It might be necessary for the application to be granted a
* {@link javax.security.auth.PrivateCredentialPermission
* PrivateCredentialPermission} if it needs to access a {@code KerberosTicket}
* instance from a {@code Subject}. This permission is not needed when the
* application depends on the default JGSS Kerberos mechanism to access the
* {@code KerberosTicket}. In that case, however, the application will need an
* appropriate
* {@link javax.security.auth.kerberos.ServicePermission ServicePermission}.
* <p>
* Note that this class is applicable to both ticket granting tickets and
* other regular service tickets. A ticket granting ticket is just a
* special case of a more generalized service ticket.
*
* @implNote The JAAS login module in the JDK reference implementation destroys
* all tickets after logout.
*
* @see javax.security.auth.Subject
* @see javax.security.auth.PrivateCredentialPermission
* @see javax.security.auth.login.LoginContext
* @see org.ietf.jgss.GSSCredential
* @see org.ietf.jgss.GSSManager
*
* @author Mayank Upadhyay
* @since 1.4
*/
public class KerberosTicket implements Destroyable, Refreshable,
java.io.Serializable {
private static final long serialVersionUID = 7395334370157380539L;
// XXX Make these flag indices public
private static final int FORWARDABLE_TICKET_FLAG = 1;
private static final int FORWARDED_TICKET_FLAG = 2;
private static final int PROXIABLE_TICKET_FLAG = 3;
private static final int PROXY_TICKET_FLAG = 4;
private static final int POSTDATED_TICKET_FLAG = 6;
private static final int RENEWABLE_TICKET_FLAG = 8;
private static final int INITIAL_TICKET_FLAG = 9;
private static final int NUM_FLAGS = 32;
/**
*
* ASN.1 DER Encoding of the Ticket as defined in the
* Kerberos Protocol Specification RFC4120.
*
* @serial
*/
private byte[] asn1Encoding;
/**
*{@code KeyImpl} is serialized by writing out the ASN1 Encoded bytes
* of the encryption key. The ASN1 encoding is defined in RFC4120 and as
* follows:
* <pre>
* EncryptionKey ::= SEQUENCE {
* keytype [0] Int32 -- actually encryption type --,
* keyvalue [1] OCTET STRING
* }
* </pre>
*
* @serial
*/
private KeyImpl sessionKey;
/**
*
* Ticket Flags as defined in the Kerberos Protocol Specification RFC4120.
*
* @serial
*/
private boolean[] flags;
/**
*
* Time of initial authentication
*
* @serial
*/
private Date authTime;
/**
*
* Time after which the ticket is valid.
* @serial
*/
private Date startTime;
/**
*
* Time after which the ticket will not be honored. (its expiration time).
*
* @serial
*/
private Date endTime;
/**
*
* For renewable Tickets it indicates the maximum endtime that may be
* included in a renewal. It can be thought of as the absolute expiration
* time for the ticket, including all renewals. This field may be null
* for tickets that are not renewable.
*
* @serial
*/
private Date renewTill;
/**
*
* Client that owns the service ticket
*
* @serial
*/
private KerberosPrincipal client;
/**
*
* The service for which the ticket was issued.
*
* @serial
*/
private KerberosPrincipal server;
/**
*
* The addresses from where the ticket may be used by the client.
* This field may be null when the ticket is usable from any address.
*
* @serial
*/
private InetAddress[] clientAddresses;
private transient boolean destroyed = false;
/**
* Constructs a {@code KerberosTicket} using credentials information that a
* client either receives from a KDC or reads from a cache.
*
* @param asn1Encoding the ASN.1 encoding of the ticket as defined by
* the Kerberos protocol specification.
* @param client the client that owns this service
* ticket
* @param server the service that this ticket is for
* @param sessionKey the raw bytes for the session key that must be
* used to encrypt the authenticator that will be sent to the server
* @param keyType the key type for the session key as defined by the
* Kerberos protocol specification.
* @param flags the ticket flags. Each element in this array indicates
* the value for the corresponding bit in the ASN.1 BitString that
* represents the ticket flags. If the number of elements in this array
* is less than the number of flags used by the Kerberos protocol,
* then the missing flags will be filled in with false.
* @param authTime the time of initial authentication for the client
* @param startTime the time after which the ticket will be valid. This
* may be null in which case the value of authTime is treated as the
* startTime.
* @param endTime the time after which the ticket will no longer be
* valid
* @param renewTill an absolute expiration time for the ticket,
* including all renewal that might be possible. This field may be null
* for tickets that are not renewable.
* @param clientAddresses the addresses from where the ticket may be
* used by the client. This field may be null when the ticket is usable
* from any address.
*/
public KerberosTicket(byte[] asn1Encoding,
KerberosPrincipal client,
KerberosPrincipal server,
byte[] sessionKey,
int keyType,
boolean[] flags,
Date authTime,
Date startTime,
Date endTime,
Date renewTill,
InetAddress[] clientAddresses) {
init(asn1Encoding, client, server, sessionKey, keyType, flags,
authTime, startTime, endTime, renewTill, clientAddresses);
}
private void init(byte[] asn1Encoding,
KerberosPrincipal client,
KerberosPrincipal server,
byte[] sessionKey,
int keyType,
boolean[] flags,
Date authTime,
Date startTime,
Date endTime,
Date renewTill,
InetAddress[] clientAddresses) {
if (sessionKey == null) {
throw new IllegalArgumentException("Session key for ticket"
+ " cannot be null");
}
init(asn1Encoding, client, server,
new KeyImpl(sessionKey, keyType), flags, authTime,
startTime, endTime, renewTill, clientAddresses);
}
private void init(byte[] asn1Encoding,
KerberosPrincipal client,
KerberosPrincipal server,
KeyImpl sessionKey,
boolean[] flags,
Date authTime,
Date startTime,
Date endTime,
Date renewTill,
InetAddress[] clientAddresses) {
if (asn1Encoding == null) {
throw new IllegalArgumentException("ASN.1 encoding of ticket"
+ " cannot be null");
}
this.asn1Encoding = asn1Encoding.clone();
if (client == null) {
throw new IllegalArgumentException("Client name in ticket"
+ " cannot be null");
}
this.client = client;
if (server == null) {
throw new IllegalArgumentException("Server name in ticket"
+ " cannot be null");
}
this.server = server;
// Caller needs to make sure `sessionKey` will not be null
this.sessionKey = sessionKey;
if (flags != null) {
if (flags.length >= NUM_FLAGS) {
this.flags = flags.clone();
} else {
this.flags = new boolean[NUM_FLAGS];
// Fill in whatever we have
for (int i = 0; i < flags.length; i++) {
this.flags[i] = flags[i];
}
}
} else {
this.flags = new boolean[NUM_FLAGS];
}
if (this.flags[RENEWABLE_TICKET_FLAG] && renewTill != null) {
this.renewTill = new Date(renewTill.getTime());
}
if (authTime != null) {
this.authTime = new Date(authTime.getTime());
}
if (startTime != null) {
this.startTime = new Date(startTime.getTime());
} else {
this.startTime = this.authTime;
}
if (endTime == null) {
throw new IllegalArgumentException("End time for ticket validity"
+ " cannot be null");
}
this.endTime = new Date(endTime.getTime());
if (clientAddresses != null) {
this.clientAddresses = clientAddresses.clone();
}
}
/**
* Returns the client principal associated with this ticket.
*
* @return the client principal, or {@code null} if destroyed.
*/
public final KerberosPrincipal getClient() {
return client;
}
/**
* Returns the service principal associated with this ticket.
*
* @return the service principal, or {@code null} if destroyed.
*/
public final KerberosPrincipal getServer() {
return server;
}
/**
* Returns the session key associated with this ticket. The return value
* is always a {@link EncryptionKey} object.
*
* @return the session key.
* @throws IllegalStateException if this ticket is destroyed
*/
public final SecretKey getSessionKey() {
if (destroyed) {
throw new IllegalStateException("This ticket is no longer valid");
}
return new EncryptionKey(
sessionKey.getEncoded(), sessionKey.getKeyType());
}
/**
* Returns the key type of the session key associated with this
* ticket as defined by the Kerberos Protocol Specification.
*
* @return the key type of the session key associated with this
* ticket.
* @throws IllegalStateException if this ticket is destroyed
*
* @see #getSessionKey()
*/
public final int getSessionKeyType() {
if (destroyed) {
throw new IllegalStateException("This ticket is no longer valid");
}
return sessionKey.getKeyType();
}
/**
* Determines if this ticket is forwardable.
*
* @return true if this ticket is forwardable, or false if not forwardable
* or destroyed.
*/
public final boolean isForwardable() {
return flags == null? false: flags[FORWARDABLE_TICKET_FLAG];
}
/**
* Determines if this ticket had been forwarded or was issued based on
* authentication involving a forwarded ticket-granting ticket.
*
* @return true if this ticket had been forwarded or was issued based on
* authentication involving a forwarded ticket-granting ticket,
* or false otherwise or destroyed.
*/
public final boolean isForwarded() {
return flags == null? false: flags[FORWARDED_TICKET_FLAG];
}
/**
* Determines if this ticket is proxiable.
*
* @return true if this ticket is proxiable, or false if not proxiable
* or destroyed.
*/
public final boolean isProxiable() {
return flags == null? false: flags[PROXIABLE_TICKET_FLAG];
}
/**
* Determines is this ticket is a proxy-ticket.
*
* @return true if this ticket is a proxy-ticket, or false if not
* a proxy-ticket or destroyed.
*/
public final boolean isProxy() {
return flags == null? false: flags[PROXY_TICKET_FLAG];
}
/**
* Determines is this ticket is post-dated.
*
* @return true if this ticket is post-dated, or false if not post-dated
* or destroyed.
*/
public final boolean isPostdated() {
return flags == null? false: flags[POSTDATED_TICKET_FLAG];
}
/**
* Determines is this ticket is renewable. If so, the {@link #refresh()
* refresh} method can be called, assuming the validity period for
* renewing is not already over.
*
* @return true if this ticket is renewable, or false if not renewable
* or destroyed.
*/
public final boolean isRenewable() {
return flags == null? false: flags[RENEWABLE_TICKET_FLAG];
}
/**
* Determines if this ticket was issued using the Kerberos AS-Exchange
* protocol, and not issued based on some ticket-granting ticket.
*
* @return true if this ticket was issued using the Kerberos AS-Exchange
* protocol, or false if not issued this way or destroyed.
*/
public final boolean isInitial() {
return flags == null? false: flags[INITIAL_TICKET_FLAG];
}
/**
* Returns the flags associated with this ticket. Each element in the
* returned array indicates the value for the corresponding bit in the
* ASN.1 BitString that represents the ticket flags.
*
* @return the flags associated with this ticket, or {@code null}
* if destroyed.
*/
public final boolean[] getFlags() {
return (flags == null? null: flags.clone());
}
/**
* Returns the time that the client was authenticated.
*
* @return the time that the client was authenticated
* or {@code null} if the field is not set or
* this ticket is destroyed.
*/
public final java.util.Date getAuthTime() {
return (authTime == null) ? null : (Date)authTime.clone();
}
/**
* Returns the start time for this ticket's validity period.
*
* @return the start time for this ticket's validity period
* or {@code null} if the field is not set or
* this ticket is destroyed.
*/
public final java.util.Date getStartTime() {
return (startTime == null) ? null : (Date)startTime.clone();
}
/**
* Returns the expiration time for this ticket's validity period.
*
* @return the expiration time for this ticket's validity period,
* or {@code null} if destroyed.
*/
public final java.util.Date getEndTime() {
return (endTime == null) ? null : (Date) endTime.clone();
}
/**
* Returns the latest expiration time for this ticket, including all
* renewals. This will return a null value for non-renewable tickets.
*
* @return the latest expiration time for this ticket, or {@code null}
* if destroyed.
*/
public final java.util.Date getRenewTill() {
return (renewTill == null) ? null: (Date)renewTill.clone();
}
/**
* Returns a list of addresses from where the ticket can be used.
*
* @return the list of addresses, or {@code null} if the field was not
* provided or this ticket is destroyed.
*/
public final java.net.InetAddress[] getClientAddresses() {
return (clientAddresses == null) ? null: clientAddresses.clone();
}
/**
* Returns an ASN.1 encoding of the entire ticket.
*
* @return an ASN.1 encoding of the entire ticket. A new byte
* array is returned each time this method is called.
* @throws IllegalStateException if this ticket is destroyed
*/
public final byte[] getEncoded() {
if (destroyed) {
throw new IllegalStateException("This ticket is no longer valid");
}
return asn1Encoding.clone();
}
/**
* Determines if this ticket is still current.
*
* @return true if this ticket is still current, or false if not current
* or destroyed.
*/
public boolean isCurrent() {
return endTime == null? false: (System.currentTimeMillis() <= endTime.getTime());
}
/**
* Extends the validity period of this ticket. The ticket will contain
* a new session key if the refresh operation succeeds. The refresh
* operation will fail if the ticket is not renewable or the latest
* allowable renew time has passed. Any other error returned by the
* KDC will also cause this method to fail.
*
* Note: This method is not synchronized with the accessor
* methods of this object. Hence callers need to be aware of multiple
* threads that might access this and try to renew it at the same
* time.
*
* @throws IllegalStateException if this ticket is destroyed
* @throws RefreshFailedException if the ticket is not renewable, or
* the latest allowable renew time has passed, or the KDC returns some
* error.
*
* @see #isRenewable()
* @see #getRenewTill()
*/
public void refresh() throws RefreshFailedException {
if (destroyed) {
throw new RefreshFailedException("A destroyed ticket "
+ "cannot be renewd.");
}
if (!isRenewable()) {
throw new RefreshFailedException("This ticket is not renewable");
}
if (getRenewTill() == null) {
// Renewable ticket without renew-till. Illegal and ignored.
return;
}
if (System.currentTimeMillis() > getRenewTill().getTime()) {
throw new RefreshFailedException("This ticket is past "
+ "its last renewal time.");
}
Throwable e = null;
sun.security.krb5.Credentials krb5Creds = null;
try {
krb5Creds = new sun.security.krb5.Credentials(asn1Encoding,
client.getName(),
server.getName(),
sessionKey.getEncoded(),
sessionKey.getKeyType(),
flags,
authTime,
startTime,
endTime,
renewTill,
clientAddresses);
krb5Creds = krb5Creds.renew();
} catch (sun.security.krb5.KrbException krbException) {
e = krbException;
} catch (java.io.IOException ioException) {
e = ioException;
}
if (e != null) {
RefreshFailedException rfException
= new RefreshFailedException("Failed to renew Kerberos Ticket "
+ "for client " + client
+ " and server " + server
+ " - " + e.getMessage());
rfException.initCause(e);
throw rfException;
}
/*
* In case multiple threads try to refresh it at the same time.
*/
synchronized (this) {
try {
this.destroy();
} catch (DestroyFailedException dfException) {
// Squelch it since we don't care about the old ticket.
}
init(krb5Creds.getEncoded(),
new KerberosPrincipal(krb5Creds.getClient().getName()),
new KerberosPrincipal(krb5Creds.getServer().getName(),
KerberosPrincipal.KRB_NT_SRV_INST),
krb5Creds.getSessionKey().getBytes(),
krb5Creds.getSessionKey().getEType(),
krb5Creds.getFlags(),
krb5Creds.getAuthTime(),
krb5Creds.getStartTime(),
krb5Creds.getEndTime(),
krb5Creds.getRenewTill(),
krb5Creds.getClientAddresses());
destroyed = false;
}
}
/**
* Destroys the ticket and destroys any sensitive information stored in
* it.
*/
public void destroy() throws DestroyFailedException {
if (!destroyed) {
Arrays.fill(asn1Encoding, (byte) 0);
client = null;
server = null;
sessionKey.destroy();
flags = null;
authTime = null;
startTime = null;
endTime = null;
renewTill = null;
clientAddresses = null;
destroyed = true;
}
}
/**
* Determines if this ticket has been destroyed.
*/
public boolean isDestroyed() {
return destroyed;
}
/**
* Returns an informative textual representation of this {@code KerberosTicket}.
*
* @return an informative textual representation of this {@code KerberosTicket}.
*/
public String toString() {
if (destroyed) {
return "Destroyed KerberosTicket";
}
StringBuilder caddrString = new StringBuilder();
if (clientAddresses != null) {
for (int i = 0; i < clientAddresses.length; i++) {
caddrString.append("clientAddresses[" + i + "] = " +
clientAddresses[i].toString());
}
}
return ("Ticket (hex) = " + "\n" +
(new HexDumpEncoder()).encodeBuffer(asn1Encoding) + "\n" +
"Client Principal = " + client.toString() + "\n" +
"Server Principal = " + server.toString() + "\n" +
"Session Key = " + sessionKey.toString() + "\n" +
"Forwardable Ticket " + flags[FORWARDABLE_TICKET_FLAG] + "\n" +
"Forwarded Ticket " + flags[FORWARDED_TICKET_FLAG] + "\n" +
"Proxiable Ticket " + flags[PROXIABLE_TICKET_FLAG] + "\n" +
"Proxy Ticket " + flags[PROXY_TICKET_FLAG] + "\n" +
"Postdated Ticket " + flags[POSTDATED_TICKET_FLAG] + "\n" +
"Renewable Ticket " + flags[RENEWABLE_TICKET_FLAG] + "\n" +
"Initial Ticket " + flags[INITIAL_TICKET_FLAG] + "\n" +
"Auth Time = " + String.valueOf(authTime) + "\n" +
"Start Time = " + String.valueOf(startTime) + "\n" +
"End Time = " + endTime.toString() + "\n" +
"Renew Till = " + String.valueOf(renewTill) + "\n" +
"Client Addresses " +
(clientAddresses == null ? " Null " : caddrString.toString() +
"\n"));
}
/**
* Returns a hash code for this {@code KerberosTicket}.
*
* @return a hash code for this {@code KerberosTicket}.
* @since 1.6
*/
public int hashCode() {
int result = 17;
if (isDestroyed()) {
return result;
}
result = result * 37 + Arrays.hashCode(getEncoded());
result = result * 37 + endTime.hashCode();
result = result * 37 + client.hashCode();
result = result * 37 + server.hashCode();
result = result * 37 + sessionKey.hashCode();
// authTime may be null
if (authTime != null) {
result = result * 37 + authTime.hashCode();
}
// startTime may be null
if (startTime != null) {
result = result * 37 + startTime.hashCode();
}
// renewTill may be null
if (renewTill != null) {
result = result * 37 + renewTill.hashCode();
}
// clientAddress may be null, the array's hashCode is 0
result = result * 37 + Arrays.hashCode(clientAddresses);
return result * 37 + Arrays.hashCode(flags);
}
/**
* Compares the specified object with this {@code KerberosTicket} for equality.
* Returns true if the given object is also a
* {@code KerberosTicket} and the two
* {@code KerberosTicket} instances are equivalent.
* A destroyed {@code KerberosTicket} object is only equal to itself.
*
* @param other the object to compare to
* @return true if the specified object is equal to this {@code KerberosTicket},
* false otherwise.
* @since 1.6
*/
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (! (other instanceof KerberosTicket)) {
return false;
}
KerberosTicket otherTicket = ((KerberosTicket) other);
if (isDestroyed() || otherTicket.isDestroyed()) {
return false;
}
if (!Arrays.equals(getEncoded(), otherTicket.getEncoded()) ||
!endTime.equals(otherTicket.getEndTime()) ||
!server.equals(otherTicket.getServer()) ||
!client.equals(otherTicket.getClient()) ||
!sessionKey.equals(otherTicket.sessionKey) ||
!Arrays.equals(clientAddresses, otherTicket.getClientAddresses()) ||
!Arrays.equals(flags, otherTicket.getFlags())) {
return false;
}
// authTime may be null
if (authTime == null) {
if (otherTicket.getAuthTime() != null) {
return false;
}
} else {
if (!authTime.equals(otherTicket.getAuthTime())) {
return false;
}
}
// startTime may be null
if (startTime == null) {
if (otherTicket.getStartTime() != null) {
return false;
}
} else {
if (!startTime.equals(otherTicket.getStartTime())) {
return false;
}
}
if (renewTill == null) {
if (otherTicket.getRenewTill() != null) {
return false;
}
} else {
if (!renewTill.equals(otherTicket.getRenewTill())) {
return false;
}
}
return true;
}
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
if (sessionKey == null) {
throw new InvalidObjectException("Session key cannot be null");
}
try {
init(asn1Encoding, client, server, sessionKey,
flags, authTime, startTime, endTime,
renewTill, clientAddresses);
} catch (IllegalArgumentException iae) {
throw (InvalidObjectException)
new InvalidObjectException(iae.getMessage()).initCause(iae);
}
}
}

View file

@ -0,0 +1,248 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.security.auth.kerberos;
import java.io.*;
import java.util.Arrays;
import javax.crypto.SecretKey;
import javax.security.auth.Destroyable;
import javax.security.auth.DestroyFailedException;
import sun.security.util.HexDumpEncoder;
import sun.security.krb5.Asn1Exception;
import sun.security.krb5.PrincipalName;
import sun.security.krb5.EncryptionKey;
import sun.security.krb5.EncryptedData;
import sun.security.krb5.KrbException;
import sun.security.util.DerValue;
/**
* This class encapsulates a Kerberos encryption key. It is not associated
* with a principal and may represent an ephemeral session key.
*
* @author Mayank Upadhyay
* @since 1.4
*
* @serial include
*/
class KeyImpl implements SecretKey, Destroyable, Serializable {
private static final long serialVersionUID = -7889313790214321193L;
private transient byte[] keyBytes;
private transient int keyType;
private transient volatile boolean destroyed = false;
/**
* Constructs a KeyImpl from the given bytes.
*
* @param keyBytes the raw bytes for the secret key
* @param keyType the key type for the secret key as defined by the
* Kerberos protocol specification.
*/
public KeyImpl(byte[] keyBytes,
int keyType) {
this.keyBytes = keyBytes.clone();
this.keyType = keyType;
}
/**
* Constructs a KeyImpl from a password.
*
* @param principal the principal from which to derive the salt
* @param password the password that should be used to compute the
* key.
* @param algorithm the name for the algorithm that this key wil be
* used for. This parameter may be null in which case "DES" will be
* assumed.
*/
public KeyImpl(KerberosPrincipal principal,
char[] password,
String algorithm) {
try {
PrincipalName princ = new PrincipalName(principal.getName());
EncryptionKey key;
if ("none".equalsIgnoreCase(algorithm)) {
key = EncryptionKey.NULL_KEY;
} else {
key = new EncryptionKey(password, princ.getSalt(), algorithm);
}
this.keyBytes = key.getBytes();
this.keyType = key.getEType();
} catch (KrbException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
/**
* Returns the keyType for this key as defined in the Kerberos Spec.
*/
public final int getKeyType() {
if (destroyed)
throw new IllegalStateException("This key is no longer valid");
return keyType;
}
/*
* Methods from java.security.Key
*/
public final String getAlgorithm() {
return getAlgorithmName(keyType);
}
private String getAlgorithmName(int eType) {
if (destroyed)
throw new IllegalStateException("This key is no longer valid");
switch (eType) {
case EncryptedData.ETYPE_DES_CBC_CRC:
return "des-cbc-crc";
case EncryptedData.ETYPE_DES_CBC_MD5:
return "des-cbc-md5";
case EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD:
return "des3-cbc-sha1-kd";
case EncryptedData.ETYPE_ARCFOUR_HMAC:
return "rc4-hmac";
case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
return "aes128-cts-hmac-sha1-96";
case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
return "aes256-cts-hmac-sha1-96";
case EncryptedData.ETYPE_NULL:
return "none";
default:
return eType > 0 ? "unknown" : "private";
}
}
public final String getFormat() {
if (destroyed)
throw new IllegalStateException("This key is no longer valid");
return "RAW";
}
public final byte[] getEncoded() {
if (destroyed)
throw new IllegalStateException("This key is no longer valid");
return keyBytes.clone();
}
public void destroy() throws DestroyFailedException {
if (!destroyed) {
destroyed = true;
Arrays.fill(keyBytes, (byte) 0);
}
}
public boolean isDestroyed() {
return destroyed;
}
/**
* @serialData this {@code KeyImpl} is serialized by
* writing out the ASN1 Encoded bytes of the encryption key.
* The ASN1 encoding is defined in RFC4120 and as follows:
* EncryptionKey ::= SEQUENCE {
* keytype [0] Int32 -- actually encryption type --,
* keyvalue [1] OCTET STRING
* }
*/
private void writeObject(ObjectOutputStream ois)
throws IOException {
if (destroyed) {
throw new IOException("This key is no longer valid");
}
try {
ois.writeObject((new EncryptionKey(keyType, keyBytes)).asn1Encode());
} catch (Asn1Exception ae) {
throw new IOException(ae.getMessage());
}
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
try {
EncryptionKey encKey = new EncryptionKey(new
DerValue((byte[])ois.readObject()));
keyType = encKey.getEType();
keyBytes = encKey.getBytes();
} catch (Asn1Exception ae) {
throw new IOException(ae.getMessage());
}
}
public String toString() {
HexDumpEncoder hd = new HexDumpEncoder();
return "EncryptionKey: keyType=" + keyType
+ " keyBytes (hex dump)="
+ (keyBytes == null || keyBytes.length == 0 ?
" Empty Key" :
'\n' + hd.encodeBuffer(keyBytes)
+ '\n');
}
public int hashCode() {
int result = 17;
if(isDestroyed()) {
return result;
}
result = 37 * result + Arrays.hashCode(keyBytes);
return 37 * result + keyType;
}
public boolean equals(Object other) {
if (other == this)
return true;
if (! (other instanceof KeyImpl)) {
return false;
}
KeyImpl otherKey = ((KeyImpl) other);
if (isDestroyed() || otherKey.isDestroyed()) {
return false;
}
if(keyType != otherKey.getKeyType() ||
!Arrays.equals(keyBytes, otherKey.getEncoded())) {
return false;
}
return true;
}
}

View file

@ -0,0 +1,385 @@
/*
* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.security.auth.kerberos;
import java.io.File;
import java.security.AccessControlException;
import java.util.Objects;
import sun.security.krb5.EncryptionKey;
import sun.security.krb5.KerberosSecrets;
import sun.security.krb5.PrincipalName;
import sun.security.krb5.RealmException;
/**
* This class encapsulates a keytab file.
* <p>
* A Kerberos JAAS login module that obtains long term secret keys from a
* keytab file should use this class. The login module will store
* an instance of this class in the private credential set of a
* {@link javax.security.auth.Subject Subject} during the commit phase of the
* authentication process.
* <p>
* If a {@code KeyTab} object is obtained from {@link #getUnboundInstance()}
* or {@link #getUnboundInstance(java.io.File)}, it is unbound and thus can be
* used by any service principal. Otherwise, if it's obtained from
* {@link #getInstance(KerberosPrincipal)} or
* {@link #getInstance(KerberosPrincipal, java.io.File)}, it is bound to the
* specific service principal and can only be used by it.
* <p>
* Please note the constructors {@link #getInstance()} and
* {@link #getInstance(java.io.File)} were created when there was no support
* for unbound keytabs. These methods should not be used anymore. An object
* created with either of these methods are considered to be bound to an
* unknown principal, which means, its {@link #isBound()} returns true and
* {@link #getPrincipal()} returns null.
* <p>
* It might be necessary for the application to be granted a
* {@link javax.security.auth.PrivateCredentialPermission
* PrivateCredentialPermission} if it needs to access the {@code KeyTab}
* instance from a {@code Subject}. This permission is not needed when the
* application depends on the default JGSS Kerberos mechanism to access the
* {@code KeyTab}. In that case, however, the application will need an appropriate
* {@link javax.security.auth.kerberos.ServicePermission ServicePermission}.
* <p>
* The keytab file format is described at
* <a href="http://www.ioplex.com/utilities/keytab.txt">
* http://www.ioplex.com/utilities/keytab.txt</a>.
*
* @since 1.7
*/
public final class KeyTab {
/*
* Impl notes:
*
* This class is only a name, a permanent link to the keytab source
* (can be missing). Itself has no content. In order to read content,
* take a snapshot and read from it.
*
* The snapshot is of type sun.security.krb5.internal.ktab.KeyTab, which
* contains the content of the keytab file when the snapshot is taken.
* Itself has no refresh function and mostly an immutable class (except
* for the create/add/save methods only used by the ktab command).
*/
// Source, null if using the default one. Note that the default name
// is maintained in snapshot, this field is never "resolved".
private final File file;
// Bound user: normally from the "principal" value in a JAAS krb5
// login conf. Will be null if it's "*".
private final KerberosPrincipal princ;
private final boolean bound;
// Set up JavaxSecurityAuthKerberosAccess in KerberosSecrets
static {
KerberosSecrets.setJavaxSecurityAuthKerberosAccess(
new JavaxSecurityAuthKerberosAccessImpl());
}
private KeyTab(KerberosPrincipal princ, File file, boolean bound) {
this.princ = princ;
this.file = file;
this.bound = bound;
}
/**
* Returns a {@code KeyTab} instance from a {@code File} object
* that is bound to an unknown service principal.
* <p>
* The result of this method is never null. This method only associates
* the returned {@code KeyTab} object with the file and does not read it.
* <p>
* Developers should call {@link #getInstance(KerberosPrincipal,File)}
* when the bound service principal is known.
* @param file the keytab {@code File} object, must not be null
* @return the keytab instance
* @throws NullPointerException if the {@code file} argument is null
*/
public static KeyTab getInstance(File file) {
if (file == null) {
throw new NullPointerException("file must be non null");
}
return new KeyTab(null, file, true);
}
/**
* Returns an unbound {@code KeyTab} instance from a {@code File}
* object.
* <p>
* The result of this method is never null. This method only associates
* the returned {@code KeyTab} object with the file and does not read it.
* @param file the keytab {@code File} object, must not be null
* @return the keytab instance
* @throws NullPointerException if the file argument is null
* @since 1.8
*/
public static KeyTab getUnboundInstance(File file) {
if (file == null) {
throw new NullPointerException("file must be non null");
}
return new KeyTab(null, file, false);
}
/**
* Returns a {@code KeyTab} instance from a {@code File} object
* that is bound to the specified service principal.
* <p>
* The result of this method is never null. This method only associates
* the returned {@code KeyTab} object with the file and does not read it.
* @param princ the bound service principal, must not be null
* @param file the keytab {@code File} object, must not be null
* @return the keytab instance
* @throws NullPointerException if either of the arguments is null
* @since 1.8
*/
public static KeyTab getInstance(KerberosPrincipal princ, File file) {
if (princ == null) {
throw new NullPointerException("princ must be non null");
}
if (file == null) {
throw new NullPointerException("file must be non null");
}
return new KeyTab(princ, file, true);
}
/**
* Returns the default {@code KeyTab} instance that is bound
* to an unknown service principal.
* <p>
* The result of this method is never null. This method only associates
* the returned {@code KeyTab} object with the default keytab file and
* does not read it.
* <p>
* Developers should call {@link #getInstance(KerberosPrincipal)}
* when the bound service principal is known.
* @return the default keytab instance.
*/
public static KeyTab getInstance() {
return new KeyTab(null, null, true);
}
/**
* Returns the default unbound {@code KeyTab} instance.
* <p>
* The result of this method is never null. This method only associates
* the returned {@code KeyTab} object with the default keytab file and
* does not read it.
* @return the default keytab instance
* @since 1.8
*/
public static KeyTab getUnboundInstance() {
return new KeyTab(null, null, false);
}
/**
* Returns the default {@code KeyTab} instance that is bound
* to the specified service principal.
* <p>
* The result of this method is never null. This method only associates
* the returned {@code KeyTab} object with the default keytab file and
* does not read it.
* @param princ the bound service principal, must not be null
* @return the default keytab instance
* @throws NullPointerException if {@code princ} is null
* @since 1.8
*/
public static KeyTab getInstance(KerberosPrincipal princ) {
if (princ == null) {
throw new NullPointerException("princ must be non null");
}
return new KeyTab(princ, null, true);
}
// Takes a snapshot of the keytab content. This method is called by
// JavaxSecurityAuthKerberosAccessImpl so no more private
sun.security.krb5.internal.ktab.KeyTab takeSnapshot() {
try {
return sun.security.krb5.internal.ktab.KeyTab.getInstance(file);
} catch (AccessControlException ace) {
if (file != null) {
// It's OK to show the name if caller specified it
throw ace;
} else {
AccessControlException ace2 = new AccessControlException(
"Access to default keytab denied (modified exception)");
ace2.setStackTrace(ace.getStackTrace());
throw ace2;
}
}
}
/**
* Returns fresh keys for the given Kerberos principal.
* <p>
* Implementation of this method should make sure the returned keys match
* the latest content of the keytab file. The result is a newly created
* copy that can be modified by the caller without modifying the keytab
* object. The caller should {@link KerberosKey#destroy() destroy} the
* result keys after they are used.
* <p>
* Please note that the keytab file can be created after the
* {@code KeyTab} object is instantiated and its content may change over
* time. Therefore, an application should call this method only when it
* needs to use the keys. Any previous result from an earlier invocation
* could potentially be expired.
* <p>
* If there is any error (say, I/O error or format error)
* during the reading process of the keytab file, a saved result should be
* returned. If there is no saved result (say, this is the first time this
* method is called, or, all previous read attempts failed), an empty array
* should be returned. This can make sure the result is not drastically
* changed during the (probably slow) update of the keytab file.
* <p>
* Each time this method is called and the reading of the file succeeds
* with no exception (say, I/O error or file format error),
* the result should be saved for {@code principal}. The implementation can
* also save keys for other principals having keys in the same keytab object
* if convenient.
* <p>
* Any unsupported key read from the keytab is ignored and not included
* in the result.
* <p>
* If this keytab is bound to a specific principal, calling this method on
* another principal will return an empty array.
*
* @param principal the Kerberos principal, must not be null.
* @return the keys (never null, may be empty)
* @throws NullPointerException if the {@code principal}
* argument is null
* @throws SecurityException if a security manager exists and the read
* access to the keytab file is not permitted
*/
public KerberosKey[] getKeys(KerberosPrincipal principal) {
try {
if (princ != null && !principal.equals(princ)) {
return new KerberosKey[0];
}
PrincipalName pn = new PrincipalName(principal.getName());
EncryptionKey[] keys = takeSnapshot().readServiceKeys(pn);
KerberosKey[] kks = new KerberosKey[keys.length];
for (int i=0; i<kks.length; i++) {
Integer tmp = keys[i].getKeyVersionNumber();
kks[i] = new KerberosKey(
principal,
keys[i].getBytes(),
keys[i].getEType(),
tmp == null ? 0 : tmp.intValue());
keys[i].destroy();
}
return kks;
} catch (RealmException re) {
return new KerberosKey[0];
}
}
EncryptionKey[] getEncryptionKeys(PrincipalName principal) {
return takeSnapshot().readServiceKeys(principal);
}
/**
* Checks if the keytab file exists. Implementation of this method
* should make sure that the result matches the latest status of the
* keytab file.
* <p>
* The caller can use the result to determine if it should fallback to
* another mechanism to read the keys.
* @return true if the keytab file exists; false otherwise.
* @throws SecurityException if a security manager exists and the read
* access to the keytab file is not permitted
*/
public boolean exists() {
return !takeSnapshot().isMissing();
}
/**
* Returns an informative textual representation of this {@code KeyTab}.
*
* @return an informative textual representation of this {@code KeyTab}.
*/
public String toString() {
String s = (file == null) ? "Default keytab" : file.toString();
if (!bound) return s;
else if (princ == null) return s + " for someone";
else return s + " for " + princ;
}
/**
* Returns a hash code for this {@code KeyTab}.
*
* @return a hash code for this {@code KeyTab}.
*/
public int hashCode() {
return Objects.hash(file, princ, bound);
}
/**
* Compares the specified object with this {@code KeyTab} for equality.
* Returns true if the given object is also a
* {@code KeyTab} and the two
* {@code KeyTab} instances are equivalent.
*
* @param other the object to compare to
* @return true if the specified object is equal to this {@code KeyTab}
*/
public boolean equals(Object other) {
if (other == this)
return true;
if (! (other instanceof KeyTab)) {
return false;
}
KeyTab otherKtab = (KeyTab) other;
return Objects.equals(otherKtab.princ, princ) &&
Objects.equals(otherKtab.file, file) &&
bound == otherKtab.bound;
}
/**
* Returns the service principal this {@code KeyTab} object
* is bound to. Returns {@code null} if it's not bound.
* <p>
* Please note the deprecated constructors create a {@code KeyTab} object
* bound for some unknown principal. In this case, this method also returns
* null. User can call {@link #isBound()} to verify this case.
* @return the service principal
* @since 1.8
*/
public KerberosPrincipal getPrincipal() {
return princ;
}
/**
* Returns if the keytab is bound to a principal
* @return if the keytab is bound to a principal
* @since 1.8
*/
public boolean isBound() {
return bound;
}
}

View file

@ -0,0 +1,659 @@
/*
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.security.auth.kerberos;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.security.Permission;
import java.security.PermissionCollection;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* This class is used to protect Kerberos services and the
* credentials necessary to access those services. There is a one to
* one mapping of a service principal and the credentials necessary
* to access the service. Therefore granting access to a service
* principal implicitly grants access to the credential necessary to
* establish a security context with the service principal. This
* applies regardless of whether the credentials are in a cache
* or acquired via an exchange with the KDC. The credential can
* be either a ticket granting ticket, a service ticket or a secret
* key from a key table.
* <p>
* A ServicePermission contains a service principal name and
* a list of actions which specify the context the credential can be
* used within.
* <p>
* The service principal name is the canonical name of the
* {@code KerberosPrincipal} supplying the service, that is
* the KerberosPrincipal represents a Kerberos service
* principal. This name is treated in a case sensitive manner.
* An asterisk may appear by itself, to signify any service principal.
* <p>
* Granting this permission implies that the caller can use a cached
* credential (TGT, service ticket or secret key) within the context
* designated by the action. In the case of the TGT, granting this
* permission also implies that the TGT can be obtained by an
* Authentication Service exchange.
* <p>
* Granting this permission also implies creating {@link KerberosPrincipal}
* or {@link org.ietf.jgss.GSSName GSSName} without providing a Kerberos
* realm, as long as the permission's service principal is in this realm.
* <p>
* The possible actions are:
*
* <pre>
* initiate - allow the caller to use the credential to
* initiate a security context with a service
* principal.
*
* accept - allow the caller to use the credential to
* accept security context as a particular
* principal.
* </pre>
*
* For example, to specify the permission to access to the TGT to
* initiate a security context the permission is constructed as follows:
*
* <pre>
* ServicePermission("krbtgt/EXAMPLE.COM@EXAMPLE.COM", "initiate");
* </pre>
* <p>
* To obtain a service ticket to initiate a context with the "host"
* service the permission is constructed as follows:
* <pre>
* ServicePermission("host/foo.example.com@EXAMPLE.COM", "initiate");
* </pre>
* <p>
* For a Kerberized server the action is "accept". For example, the permission
* necessary to access and use the secret key of the Kerberized "host"
* service (telnet and the likes) would be constructed as follows:
*
* <pre>
* ServicePermission("host/foo.example.com@EXAMPLE.COM", "accept");
* </pre>
*
* @since 1.4
*/
public final class ServicePermission extends Permission
implements java.io.Serializable {
private static final long serialVersionUID = -1227585031618624935L;
/**
* Initiate a security context to the specified service
*/
private final static int INITIATE = 0x1;
/**
* Accept a security context
*/
private final static int ACCEPT = 0x2;
/**
* All actions
*/
private final static int ALL = INITIATE|ACCEPT;
/**
* No actions.
*/
private final static int NONE = 0x0;
// the actions mask
private transient int mask;
/**
* the actions string.
*
* @serial
*/
private String actions; // Left null as long as possible, then
// created and re-used in the getAction function.
/**
* Create a new {@code ServicePermission}
* with the specified {@code servicePrincipal}
* and {@code action}.
*
* @param servicePrincipal the name of the service principal.
* An asterisk may appear by itself, to signify any service principal.
*
* @param action the action string
*/
public ServicePermission(String servicePrincipal, String action) {
// Note: servicePrincipal can be "@REALM" which means any principal in
// this realm implies it. action can be "-" which means any
// action implies it.
super(servicePrincipal);
init(servicePrincipal, getMask(action));
}
/**
* Creates a ServicePermission object with the specified servicePrincipal
* and a pre-calculated mask. Avoids the overhead of re-computing the mask.
* Called by ServicePermissionCollection.
*/
ServicePermission(String servicePrincipal, int mask) {
super(servicePrincipal);
init(servicePrincipal, mask);
}
/**
* Initialize the ServicePermission object.
*/
private void init(String servicePrincipal, int mask) {
if (servicePrincipal == null)
throw new NullPointerException("service principal can't be null");
if ((mask & ALL) != mask)
throw new IllegalArgumentException("invalid actions mask");
this.mask = mask;
}
/**
* Checks if this Kerberos service permission object "implies" the
* specified permission.
* <P>
* More specifically, this method returns true if all of the following
* are true (and returns false if any of them are not):
* <ul>
* <li> <i>p</i> is an instanceof {@code ServicePermission},
* <li> <i>p</i>'s actions are a proper subset of this
* {@code ServicePermission}'s actions,
* <li> <i>p</i>'s name is equal to this {@code ServicePermission}'s name
* or this {@code ServicePermission}'s name is "*".
* </ul>
*
* @param p the permission to check against.
*
* @return true if the specified permission is implied by this object,
* false if not.
*/
@Override
public boolean implies(Permission p) {
if (!(p instanceof ServicePermission))
return false;
ServicePermission that = (ServicePermission) p;
return ((this.mask & that.mask) == that.mask) &&
impliesIgnoreMask(that);
}
boolean impliesIgnoreMask(ServicePermission p) {
return ((this.getName().equals("*")) ||
this.getName().equals(p.getName()) ||
(p.getName().startsWith("@") &&
this.getName().endsWith(p.getName())));
}
/**
* Checks two ServicePermission objects for equality.
*
* @param obj the object to test for equality with this object.
*
* @return true if {@code obj} is a ServicePermission, and has the
* same service principal, and actions as this
* ServicePermission object.
*/
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (! (obj instanceof ServicePermission))
return false;
ServicePermission that = (ServicePermission) obj;
return ((this.mask & that.mask) == that.mask) &&
this.getName().equals(that.getName());
}
/**
* Returns the hash code value for this object.
*
* @return a hash code value for this object.
*/
@Override
public int hashCode() {
return (getName().hashCode() ^ mask);
}
/**
* Returns the "canonical string representation" of the actions in the
* specified mask.
* Always returns present actions in the following order:
* initiate, accept.
*
* @param mask a specific integer action mask to translate into a string
* @return the canonical string representation of the actions
*/
static String getActions(int mask)
{
StringBuilder sb = new StringBuilder();
boolean comma = false;
if ((mask & INITIATE) == INITIATE) {
if (comma) sb.append(',');
else comma = true;
sb.append("initiate");
}
if ((mask & ACCEPT) == ACCEPT) {
if (comma) sb.append(',');
else comma = true;
sb.append("accept");
}
return sb.toString();
}
/**
* Returns the canonical string representation of the actions.
* Always returns present actions in the following order:
* initiate, accept.
*/
@Override
public String getActions() {
if (actions == null)
actions = getActions(this.mask);
return actions;
}
/**
* Returns a PermissionCollection object for storing
* ServicePermission objects.
* <br>
* ServicePermission objects must be stored in a manner that
* allows them to be inserted into the collection in any order, but
* that also enables the PermissionCollection implies method to
* be implemented in an efficient (and consistent) manner.
*
* @return a new PermissionCollection object suitable for storing
* ServicePermissions.
*/
@Override
public PermissionCollection newPermissionCollection() {
return new KrbServicePermissionCollection();
}
/**
* Return the current action mask.
*
* @return the actions mask.
*/
int getMask() {
return mask;
}
/**
* Convert an action string to an integer actions mask.
*
* Note: if action is "-", action will be NONE, which means any
* action implies it.
*
* @param action the action string.
* @return the action mask
*/
private static int getMask(String action) {
if (action == null) {
throw new NullPointerException("action can't be null");
}
if (action.equals("")) {
throw new IllegalArgumentException("action can't be empty");
}
int mask = NONE;
char[] a = action.toCharArray();
if (a.length == 1 && a[0] == '-') {
return mask;
}
int i = a.length - 1;
while (i != -1) {
char c;
// skip whitespace
while ((i!=-1) && ((c = a[i]) == ' ' ||
c == '\r' ||
c == '\n' ||
c == '\f' ||
c == '\t'))
i--;
// check for the known strings
int matchlen;
if (i >= 7 && (a[i-7] == 'i' || a[i-7] == 'I') &&
(a[i-6] == 'n' || a[i-6] == 'N') &&
(a[i-5] == 'i' || a[i-5] == 'I') &&
(a[i-4] == 't' || a[i-4] == 'T') &&
(a[i-3] == 'i' || a[i-3] == 'I') &&
(a[i-2] == 'a' || a[i-2] == 'A') &&
(a[i-1] == 't' || a[i-1] == 'T') &&
(a[i] == 'e' || a[i] == 'E'))
{
matchlen = 8;
mask |= INITIATE;
} else if (i >= 5 && (a[i-5] == 'a' || a[i-5] == 'A') &&
(a[i-4] == 'c' || a[i-4] == 'C') &&
(a[i-3] == 'c' || a[i-3] == 'C') &&
(a[i-2] == 'e' || a[i-2] == 'E') &&
(a[i-1] == 'p' || a[i-1] == 'P') &&
(a[i] == 't' || a[i] == 'T'))
{
matchlen = 6;
mask |= ACCEPT;
} else {
// parse error
throw new IllegalArgumentException(
"invalid permission: " + action);
}
// make sure we didn't just match the tail of a word
// like "ackbarfaccept". Also, skip to the comma.
boolean seencomma = false;
while (i >= matchlen && !seencomma) {
switch(a[i-matchlen]) {
case ',':
seencomma = true;
break;
case ' ': case '\r': case '\n':
case '\f': case '\t':
break;
default:
throw new IllegalArgumentException(
"invalid permission: " + action);
}
i--;
}
// point i at the location of the comma minus one (or -1).
i -= matchlen;
}
return mask;
}
/**
* WriteObject is called to save the state of the ServicePermission
* to a stream. The actions are serialized, and the superclass
* takes care of the name.
*/
private void writeObject(java.io.ObjectOutputStream s)
throws IOException
{
// Write out the actions. The superclass takes care of the name
// call getActions to make sure actions field is initialized
if (actions == null)
getActions();
s.defaultWriteObject();
}
/**
* readObject is called to restore the state of the
* ServicePermission from a stream.
*/
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
// Read in the action, then initialize the rest
s.defaultReadObject();
init(getName(),getMask(actions));
}
/*
public static void main(String[] args) throws Exception {
ServicePermission this_ =
new ServicePermission(args[0], "accept");
ServicePermission that_ =
new ServicePermission(args[1], "accept,initiate");
System.out.println("-----\n");
System.out.println("this.implies(that) = " + this_.implies(that_));
System.out.println("-----\n");
System.out.println("this = "+this_);
System.out.println("-----\n");
System.out.println("that = "+that_);
System.out.println("-----\n");
KrbServicePermissionCollection nps =
new KrbServicePermissionCollection();
nps.add(this_);
nps.add(new ServicePermission("nfs/example.com@EXAMPLE.COM",
"accept"));
nps.add(new ServicePermission("host/example.com@EXAMPLE.COM",
"initiate"));
System.out.println("nps.implies(that) = " + nps.implies(that_));
System.out.println("-----\n");
Enumeration e = nps.elements();
while (e.hasMoreElements()) {
ServicePermission x =
(ServicePermission) e.nextElement();
System.out.println("nps.e = " + x);
}
}
*/
}
final class KrbServicePermissionCollection extends PermissionCollection
implements java.io.Serializable {
// Key is the service principal, value is the ServicePermission.
// Not serialized; see serialization section at end of class
private transient ConcurrentHashMap<String, Permission> perms;
public KrbServicePermissionCollection() {
perms = new ConcurrentHashMap<>();
}
/**
* Check and see if this collection of permissions implies the permissions
* expressed in "permission".
*
* @param permission the Permission object to compare
*
* @return true if "permission" is a proper subset of a permission in
* the collection, false if not.
*/
@Override
public boolean implies(Permission permission) {
if (! (permission instanceof ServicePermission))
return false;
ServicePermission np = (ServicePermission) permission;
int desired = np.getMask();
if (desired == 0) {
for (Permission p: perms.values()) {
ServicePermission sp = (ServicePermission)p;
if (sp.impliesIgnoreMask(np)) {
return true;
}
}
return false;
}
// first, check for wildcard principal
ServicePermission x = (ServicePermission)perms.get("*");
if (x != null) {
if ((x.getMask() & desired) == desired) {
return true;
}
}
// otherwise, check for match on principal
x = (ServicePermission)perms.get(np.getName());
if (x != null) {
//System.out.println(" trying "+x);
if ((x.getMask() & desired) == desired) {
return true;
}
}
return false;
}
/**
* Adds a permission to the ServicePermissions. The key for
* the hash is the name.
*
* @param permission the Permission object to add.
*
* @exception IllegalArgumentException - if the permission is not a
* ServicePermission
*
* @exception SecurityException - if this PermissionCollection object
* has been marked readonly
*/
@Override
public void add(Permission permission) {
if (! (permission instanceof ServicePermission))
throw new IllegalArgumentException("invalid permission: "+
permission);
if (isReadOnly())
throw new SecurityException("attempt to add a Permission to a readonly PermissionCollection");
ServicePermission sp = (ServicePermission)permission;
String princName = sp.getName();
// Add permission to map if it is absent, or replace with new
// permission if applicable. NOTE: cannot use lambda for
// remappingFunction parameter until JDK-8076596 is fixed.
perms.merge(princName, sp,
new java.util.function.BiFunction<>() {
@Override
public Permission apply(Permission existingVal,
Permission newVal) {
int oldMask = ((ServicePermission)existingVal).getMask();
int newMask = ((ServicePermission)newVal).getMask();
if (oldMask != newMask) {
int effective = oldMask | newMask;
if (effective == newMask) {
return newVal;
}
if (effective != oldMask) {
return new ServicePermission(princName, effective);
}
}
return existingVal;
}
}
);
}
/**
* Returns an enumeration of all the ServicePermission objects
* in the container.
*
* @return an enumeration of all the ServicePermission objects.
*/
@Override
public Enumeration<Permission> elements() {
return perms.elements();
}
private static final long serialVersionUID = -4118834211490102011L;
// Need to maintain serialization interoperability with earlier releases,
// which had the serializable field:
// private Vector permissions;
/**
* @serialField permissions java.util.Vector
* A list of ServicePermission objects.
*/
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("permissions", Vector.class),
};
/**
* @serialData "permissions" field (a Vector containing the ServicePermissions).
*/
/*
* Writes the contents of the perms field out as a Vector for
* serialization compatibility with earlier releases.
*/
private void writeObject(ObjectOutputStream out) throws IOException {
// Don't call out.defaultWriteObject()
// Write out Vector
Vector<Permission> permissions = new Vector<>(perms.values());
ObjectOutputStream.PutField pfields = out.putFields();
pfields.put("permissions", permissions);
out.writeFields();
}
/*
* Reads in a Vector of ServicePermissions and saves them in the perms field.
*/
@SuppressWarnings("unchecked")
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException
{
// Don't call defaultReadObject()
// Read in serialized fields
ObjectInputStream.GetField gfields = in.readFields();
// Get the one we want
Vector<Permission> permissions =
(Vector<Permission>)gfields.get("permissions", null);
perms = new ConcurrentHashMap<>(permissions.size());
for (Permission perm : permissions) {
perms.put(perm.getName(), perm);
}
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* This package contains utility classes related to the Kerberos network
* authentication protocol. They do not provide much Kerberos support
* themselves.<p>
*
* The Kerberos network authentication protocol is defined in
* <a href=http://www.ietf.org/rfc/rfc4120.txt>RFC 4120</a>. The Java
* platform contains support for the client side of Kerberos via the
* {@link org.ietf.jgss} package. There might also be
* a login module that implements
* {@link javax.security.auth.spi.LoginModule LoginModule} to authenticate
* Kerberos principals.<p>
*
* You can provide the name of your default realm and Key Distribution
* Center (KDC) host for that realm using the system properties
* {@code java.security.krb5.realm} and {@code java.security.krb5.kdc}.
* Both properties must be set.
* Alternatively, the {@code java.security.krb5.conf} system property can
* be set to the location of an MIT style {@code krb5.conf} configuration
* file. If none of these system properties are set, the {@code krb5.conf}
* file is searched for in an implementation-specific manner. Typically,
* an implementation will first look for a {@code krb5.conf} file in
* {@code <java-home>/conf/security} and failing that, in an OS-specific
* location.<p>
*
* The {@code krb5.conf} file is formatted in the Windows INI file style,
* which contains a series of relations grouped into different sections.
* Each relation contains a key and a value, the value can be an arbitrary
* string or a boolean value. A boolean value can be one of "true", "false",
* "yes", or "no", and values are case-insensitive.
*
* @since 1.4
*/
package javax.security.auth.kerberos;

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Defines the Java binding of the IETF Generic Security Services API (GSS-API).
* <P>
* This module also contains GSS-API mechanisms including Kerberos v5 and SPNEGO.
*
* @moduleGraph
* @since 9
*/
module java.security.jgss {
requires java.naming;
exports javax.security.auth.kerberos;
exports org.ietf.jgss;
exports sun.security.jgss to
jdk.security.jgss;
exports sun.security.jgss.krb5 to
jdk.security.auth;
exports sun.security.krb5 to
jdk.security.auth;
exports sun.security.krb5.internal to
jdk.security.jgss;
exports sun.security.krb5.internal.ktab to
jdk.security.auth;
// Opens for reflective instantiation of sun.net.www.protocol.http.spnego.NegotiatorImpl
// to sun.net.www.protocol.http.HttpURLConnection
opens sun.net.www.protocol.http.spnego to
java.base;
provides java.security.Provider with
sun.security.jgss.SunProvider;
provides sun.security.ssl.ClientKeyExchangeService with
sun.security.krb5.internal.ssl.Krb5KeyExchangeService;
}

View file

@ -0,0 +1,212 @@
/*
* Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.ietf.jgss;
import java.net.InetAddress;
import java.util.Arrays;
/**
* This class encapsulates the concept of caller-provided channel
* binding information. Channel bindings are used to strengthen the
* quality with which peer entity authentication is provided during
* context establishment. They enable the GSS-API callers to bind the
* establishment of the security context to relevant characteristics
* like addresses or to application specific data.<p>
*
* The caller initiating the security context must determine the
* appropriate channel binding values to set in the GSSContext object.
* The acceptor must provide an identical binding in order to validate
* that received tokens possess correct channel-related characteristics.<p>
*
* Use of channel bindings is optional in GSS-API. ChannelBinding can be
* set for the {@link GSSContext GSSContext} using the {@link
* GSSContext#setChannelBinding(ChannelBinding) setChannelBinding} method
* before the first call to {@link GSSContext#initSecContext(byte[], int, int)
* initSecContext} or {@link GSSContext#acceptSecContext(byte[], int, int)
* acceptSecContext} has been performed. Unless the <code>setChannelBinding</code>
* method has been used to set the ChannelBinding for a GSSContext object,
* <code>null</code> ChannelBinding will be assumed. <p>
*
* Conceptually, the GSS-API concatenates the initiator and acceptor
* address information, and the application supplied byte array to form an
* octet string. The mechanism calculates a MIC over this octet string and
* binds the MIC to the context establishment token emitted by
* <code>initSecContext</code> method of the <code>GSSContext</code>
* interface. The same bindings are set by the context acceptor for its
* <code>GSSContext</code> object and during processing of the
* <code>acceptSecContext</code> method a MIC is calculated in the same
* way. The calculated MIC is compared with that found in the token, and if
* the MICs differ, accept will throw a <code>GSSException</code> with the
* major code set to {@link GSSException#BAD_BINDINGS BAD_BINDINGS}, and
* the context will not be established. Some mechanisms may include the
* actual channel binding data in the token (rather than just a MIC);
* applications should therefore not use confidential data as
* channel-binding components.<p>
*
* Individual mechanisms may impose additional constraints on addresses
* that may appear in channel bindings. For example, a mechanism may
* verify that the initiator address field of the channel binding
* contains the correct network address of the host system. Portable
* applications should therefore ensure that they either provide correct
* information for the address fields, or omit setting of the addressing
* information.
*
* @author Mayank Upadhyay
* @since 1.4
*/
public class ChannelBinding {
private InetAddress initiator;
private InetAddress acceptor;
private byte[] appData;
/**
* Create a ChannelBinding object with user supplied address information
* and data. <code>null</code> values can be used for any fields which the
* application does not want to specify.
*
* @param initAddr the address of the context initiator.
* <code>null</code> value can be supplied to indicate that the
* application does not want to set this value.
* @param acceptAddr the address of the context
* acceptor. <code>null</code> value can be supplied to indicate that
* the application does not want to set this value.
* @param appData application supplied data to be used as part of the
* channel bindings. <code>null</code> value can be supplied to
* indicate that the application does not want to set this value.
*/
public ChannelBinding(InetAddress initAddr, InetAddress acceptAddr,
byte[] appData) {
initiator = initAddr;
acceptor = acceptAddr;
if (appData != null) {
this.appData = new byte[appData.length];
java.lang.System.arraycopy(appData, 0, this.appData, 0,
appData.length);
}
}
/**
* Creates a ChannelBinding object without any addressing information.
*
* @param appData application supplied data to be used as part of the
* channel bindings.
*/
public ChannelBinding(byte[] appData) {
this(null, null, appData);
}
/**
* Get the initiator's address for this channel binding.
*
* @return the initiator's address. <code>null</code> is returned if
* the address has not been set.
*/
public InetAddress getInitiatorAddress() {
return initiator;
}
/**
* Get the acceptor's address for this channel binding.
*
* @return the acceptor's address. null is returned if the address has
* not been set.
*/
public InetAddress getAcceptorAddress() {
return acceptor;
}
/**
* Get the application specified data for this channel binding.
*
* @return the application data being used as part of the
* ChannelBinding. <code>null</code> is returned if no application data
* has been specified for the channel binding.
*/
public byte[] getApplicationData() {
if (appData == null) {
return null;
}
byte[] retVal = new byte[appData.length];
System.arraycopy(appData, 0, retVal, 0, appData.length);
return retVal;
}
/**
* Compares two instances of ChannelBinding.
*
* @param obj another ChannelBinding to compare this one with
* @return true if the two ChannelBinding's contain
* the same values for the initiator and acceptor addresses and the
* application data.
*/
public boolean equals(Object obj) {
if (this == obj)
return true;
if (! (obj instanceof ChannelBinding))
return false;
ChannelBinding cb = (ChannelBinding) obj;
if ((initiator != null && cb.initiator == null) ||
(initiator == null && cb.initiator != null))
return false;
if (initiator != null && !initiator.equals(cb.initiator))
return false;
if ((acceptor != null && cb.acceptor == null) ||
(acceptor == null && cb.acceptor != null))
return false;
if (acceptor != null && !acceptor.equals(cb.acceptor))
return false;
return Arrays.equals(appData, cb.appData);
}
/**
* Returns a hashcode value for this ChannelBinding object.
*
* @return a hashCode value
*/
public int hashCode() {
if (initiator != null)
return initiator.hashCode();
else if (acceptor != null)
return acceptor.hashCode();
else if (appData != null)
return new String(appData).hashCode();
else
return 1;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,370 @@
/*
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.ietf.jgss;
/**
* This interface encapsulates the GSS-API credentials for an entity. A
* credential contains all the necessary cryptographic information to
* enable the creation of a context on behalf of the entity that it
* represents. It may contain multiple, distinct, mechanism specific
* credential elements, each containing information for a specific
* security mechanism, but all referring to the same entity. A credential
* may be used to perform context initiation, acceptance, or both.<p>
*
* Credentials are instantiated using one of the
* {@code createCredential} methods in the {@link GSSManager
* GSSManager} class. GSS-API credential creation is not
* intended to provide a "login to the network" function, as such a
* function would involve the creation of new credentials rather than
* merely acquiring a handle to existing credentials. The
* <a href=package-summary.html#useSubjectCredsOnly>section on credential
* acquisition</a> in the package level description describes
* how existing credentials are acquired in the Java platform. GSS-API
* implementations must impose a local access-control policy on callers to
* prevent unauthorized callers from acquiring credentials to which they
* are not entitled. <p>
*
* Applications will create a credential object passing the desired
* parameters. The application can then use the query methods to obtain
* specific information about the instantiated credential object.
* When the credential is no longer needed, the application should call
* the {@link #dispose() dispose} method to release any resources held by
* the credential object and to destroy any cryptographically sensitive
* information.<p>
*
* This example code demonstrates the creation of a GSSCredential
* implementation for a specific entity, querying of its fields, and its
* release when it is no longer needed:
* <pre>
* GSSManager manager = GSSManager.getInstance();
*
* // start by creating a name object for the entity
* GSSName name = manager.createName("myusername", GSSName.NT_USER_NAME);
*
* // now acquire credentials for the entity
* GSSCredential cred = manager.createCredential(name,
* GSSCredential.ACCEPT_ONLY);
*
* // display credential information - name, remaining lifetime,
* // and the mechanisms it has been acquired over
* System.out.println(cred.getName().toString());
* System.out.println(cred.getRemainingLifetime());
*
* Oid [] mechs = cred.getMechs();
* if (mechs != null) {
* for (int i = 0; i{@literal <} mechs.length; i++)
* System.out.println(mechs[i].toString());
* }
*
* // release system resources held by the credential
* cred.dispose();
* </pre>
*
* @see GSSManager#createCredential(int)
* @see GSSManager#createCredential(GSSName, int, Oid, int)
* @see GSSManager#createCredential(GSSName, int, Oid[], int)
* @see #dispose()
*
* @author Mayank Upadhyay
* @since 1.4
*/
public interface GSSCredential extends Cloneable{
/**
* Credential usage flag requesting that it be usable
* for both context initiation and acceptance.
*
*/
public static final int INITIATE_AND_ACCEPT = 0;
/**
* Credential usage flag requesting that it be usable
* for context initiation only.
*
*/
public static final int INITIATE_ONLY = 1;
/**
* Credential usage flag requesting that it be usable
* for context acceptance only.
*
*/
public static final int ACCEPT_ONLY = 2;
/**
* A lifetime constant representing the default credential lifetime. This
* value it set to 0.
*/
public static final int DEFAULT_LIFETIME = 0;
/**
* A lifetime constant representing indefinite credential lifetime.
* This value must is set to the maximum integer value in Java -
* {@link java.lang.Integer#MAX_VALUE Integer.MAX_VALUE}.
*/
public static final int INDEFINITE_LIFETIME = Integer.MAX_VALUE;
/**
* Releases any sensitive information that the GSSCredential object may
* be containing. Applications should call this method as soon as the
* credential is no longer needed to minimize the time any sensitive
* information is maintained.
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public void dispose() throws GSSException;
/**
* Retrieves the name of the entity that the credential asserts.
*
* @return a GSSName representing the entity
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public GSSName getName() throws GSSException;
/**
* Retrieves a Mechanism Name of the entity that the credential
* asserts. This is equivalent to calling {@link
* GSSName#canonicalize(Oid) canonicalize} on the value returned by
* the other form of {@link #getName() getName}.
*
* @param mech the Oid of the mechanism for which the Mechanism Name
* should be returned.
* @return a GSSName representing the entity canonicalized for the
* desired mechanism
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#BAD_MECH GSSException.BAD_MECH},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public GSSName getName(Oid mech) throws GSSException;
/**
* Returns the remaining lifetime in seconds for a credential. The
* remaining lifetime is the minimum lifetime amongst all of the underlying
* mechanism specific credential elements.
*
* @return the minimum remaining lifetime in seconds for this
* credential. A return value of {@link #INDEFINITE_LIFETIME
* INDEFINITE_LIFETIME} indicates that the credential does
* not expire. A return value of 0 indicates that the credential is
* already expired.
*
* @see #getRemainingInitLifetime(Oid)
* @see #getRemainingAcceptLifetime(Oid)
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public int getRemainingLifetime() throws GSSException;
/**
* Returns the lifetime in seconds for the credential to remain capable
* of initiating security contexts using the specified mechanism. This
* method queries the initiator credential element that belongs to the
* specified mechanism.
*
* @return the number of seconds remaining in the life of this credential
* element. A return value of {@link #INDEFINITE_LIFETIME
* INDEFINITE_LIFETIME} indicates that the credential element does not
* expire. A return value of 0 indicates that the credential element is
* already expired.
*
* @param mech the Oid of the mechanism whose initiator credential element
* should be queried.
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#BAD_MECH GSSException.BAD_MECH},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public int getRemainingInitLifetime(Oid mech) throws GSSException;
/**
* Returns the lifetime in seconds for the credential to remain capable
* of accepting security contexts using the specified mechanism. This
* method queries the acceptor credential element that belongs to the
* specified mechanism.
*
* @return the number of seconds remaining in the life of this credential
* element. A return value of {@link #INDEFINITE_LIFETIME
* INDEFINITE_LIFETIME} indicates that the credential element does not
* expire. A return value of 0 indicates that the credential element is
* already expired.
*
* @param mech the Oid of the mechanism whose acceptor credential element
* should be queried.
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#BAD_MECH GSSException.BAD_MECH},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public int getRemainingAcceptLifetime(Oid mech) throws GSSException;
/**
* Returns the credential usage mode. In other words, it
* tells us if this credential can be used for initiating or accepting
* security contexts. It does not tell us which mechanism(s) has to be
* used in order to do so. It is expected that an application will allow
* the GSS-API to pick a default mechanism after calling this method.
*
* @return The return value will be one of {@link #INITIATE_ONLY
* INITIATE_ONLY}, {@link #ACCEPT_ONLY ACCEPT_ONLY}, and {@link
* #INITIATE_AND_ACCEPT INITIATE_AND_ACCEPT}.
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public int getUsage() throws GSSException;
/**
* Returns the credential usage mode for a specific mechanism. In other
* words, it tells us if this credential can be used
* for initiating or accepting security contexts with a given underlying
* mechanism.
*
* @return The return value will be one of {@link #INITIATE_ONLY
* INITIATE_ONLY}, {@link #ACCEPT_ONLY ACCEPT_ONLY}, and {@link
* #INITIATE_AND_ACCEPT INITIATE_AND_ACCEPT}.
* @param mech the Oid of the mechanism whose credentials usage mode is
* to be determined.
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#BAD_MECH GSSException.BAD_MECH},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public int getUsage(Oid mech) throws GSSException;
/**
* Returns a list of mechanisms supported by this credential. It does
* not tell us which ones can be used to initiate
* contexts and which ones can be used to accept contexts. The
* application must call the {@link #getUsage(Oid) getUsage} method with
* each of the returned Oid's to determine the possible modes of
* usage.
*
* @return an array of Oid's corresponding to the supported mechanisms.
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public Oid[] getMechs() throws GSSException;
/**
* Adds a mechanism specific credential-element to an existing
* credential. This method allows the construction of credentials, one
* mechanism at a time.<p>
*
* This routine is envisioned to be used mainly by context acceptors
* during the creation of acceptor credentials which are to be used
* with a variety of clients using different security mechanisms.<p>
*
* This routine adds the new credential element "in-place". To add the
* element in a new credential, first call {@code clone} to obtain a
* copy of this credential, then call its {@code add} method.<p>
*
* As always, GSS-API implementations must impose a local access-control
* policy on callers to prevent unauthorized callers from acquiring
* credentials to which they are not entitled.
*
* Non-default values for initLifetime and acceptLifetime cannot always
* be honored by the underlying mechanisms, thus callers should be
* prepared to call {@link #getRemainingInitLifetime(Oid)
* getRemainingInitLifetime} and {@link #getRemainingAcceptLifetime(Oid)
* getRemainingAcceptLifetime} on the credential.
*
* @param name the name of the principal for whom this credential is to
* be acquired. Use {@code null} to specify the default
* principal.
* @param initLifetime the number of seconds that the credential element
* should remain valid for initiating of security contexts. Use {@link
* GSSCredential#INDEFINITE_LIFETIME GSSCredential.INDEFINITE_LIFETIME}
* to request that the credentials have the maximum permitted lifetime
* for this. Use {@link GSSCredential#DEFAULT_LIFETIME
* GSSCredential.DEFAULT_LIFETIME} to request default credential lifetime
* for this.
* @param acceptLifetime the number of seconds that the credential
* element should remain valid for accepting security contexts. Use {@link
* GSSCredential#INDEFINITE_LIFETIME GSSCredential.INDEFINITE_LIFETIME}
* to request that the credentials have the maximum permitted lifetime
* for this. Use {@link GSSCredential#DEFAULT_LIFETIME
* GSSCredential.DEFAULT_LIFETIME} to request default credential lifetime
* for this.
* @param mech the mechanism over which the credential is to be acquired.
* @param usage the usage mode that this credential
* element should add to the credential. The value
* of this parameter must be one of:
* {@link #INITIATE_AND_ACCEPT INITIATE_AND_ACCEPT},
* {@link #ACCEPT_ONLY ACCEPT_ONLY}, and
* {@link #INITIATE_ONLY INITIATE_ONLY}.
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#DUPLICATE_ELEMENT
* GSSException.DUPLICATE_ELEMENT},
* {@link GSSException#BAD_MECH GSSException.BAD_MECH},
* {@link GSSException#BAD_NAMETYPE GSSException.BAD_NAMETYPE},
* {@link GSSException#NO_CRED GSSException.NO_CRED},
* {@link GSSException#CREDENTIALS_EXPIRED
* GSSException.CREDENTIALS_EXPIRED},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public void add(GSSName name, int initLifetime, int acceptLifetime,
Oid mech, int usage) throws GSSException;
/**
* Tests if this GSSCredential asserts the same entity as the supplied
* object. The two credentials must be acquired over the same
* mechanisms and must refer to the same principal.
*
* @return {@code true} if the two GSSCredentials assert the same
* entity; {@code false} otherwise.
* @param another another GSSCredential for comparison to this one
*/
public boolean equals(Object another);
/**
* Returns a hashcode value for this GSSCredential.
*
* @return a hashCode value
*/
public int hashCode();
}

View file

@ -0,0 +1,403 @@
/*
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.ietf.jgss;
/**
* This exception is thrown whenever a GSS-API error occurs, including
* any mechanism specific error. It may contain both the major and the
* minor GSS-API status codes. Major error codes are those defined at the
* GSS-API level in this class. Minor error codes are mechanism specific
* error codes that can provide additional information. The underlying
* mechanism implementation is responsible for setting appropriate minor
* status codes when throwing this exception. Aside from delivering the
* numeric error codes to the caller, this class performs the mapping from
* their numeric values to textual representations.
*
* @author Mayank Upadhyay
* @since 1.4
*/
public class GSSException extends Exception {
private static final long serialVersionUID = -2706218945227726672L;
/**
* Channel bindings mismatch.
*/
public static final int BAD_BINDINGS = 1; //start with 1
/**
* Unsupported mechanism requested.
*/
public static final int BAD_MECH = 2;
/**
* Invalid name provided.
*/
public static final int BAD_NAME = 3;
/**
* Name of unsupported type provided.
*/
public static final int BAD_NAMETYPE = 4;
/**
* Invalid status code.
*/
/*
* This is meant to be thrown by display_status which displays
* major/minor status when an incorrect status type is passed in to it!
*/
public static final int BAD_STATUS = 5;
/**
* Token had invalid integrity check.
*/
public static final int BAD_MIC = 6;
/**
* Security context expired.
*/
public static final int CONTEXT_EXPIRED = 7;
/**
* Expired credentials.
*/
public static final int CREDENTIALS_EXPIRED = 8;
/**
* Defective credentials.
*
*/
public static final int DEFECTIVE_CREDENTIAL = 9;
/**
* Defective token.
*
*/
public static final int DEFECTIVE_TOKEN = 10;
/**
* General failure, unspecified at GSS-API level.
*/
public static final int FAILURE = 11;
/**
* Invalid security context.
*/
public static final int NO_CONTEXT = 12;
/**
* Invalid credentials.
*/
public static final int NO_CRED = 13;
/**
* Unsupported QOP value.
*/
public static final int BAD_QOP = 14;
/**
* Operation unauthorized.
*/
public static final int UNAUTHORIZED = 15;
/**
* Operation unavailable.
*/
public static final int UNAVAILABLE = 16;
/**
* Duplicate credential element requested.
*/
public static final int DUPLICATE_ELEMENT = 17;
/**
* Name contains multi-mechanism elements.
*/
public static final int NAME_NOT_MN = 18;
/**
* The token was a duplicate of an earlier token.
* This is a fatal error code that may occur during
* context establishment. It is not used to indicate
* supplementary status values. The MessageProp object is
* used for that purpose.
*/
public static final int DUPLICATE_TOKEN = 19;
/**
* The token's validity period has expired. This is a
* fatal error code that may occur during context establishment.
* It is not used to indicate supplementary status values.
* The MessageProp object is used for that purpose.
*/
public static final int OLD_TOKEN = 20;
/**
* A later token has already been processed. This is a
* fatal error code that may occur during context establishment.
* It is not used to indicate supplementary status values.
* The MessageProp object is used for that purpose.
*/
public static final int UNSEQ_TOKEN = 21;
/**
* An expected per-message token was not received. This is a
* fatal error code that may occur during context establishment.
* It is not used to indicate supplementary status values.
* The MessageProp object is used for that purpose.
*/
public static final int GAP_TOKEN = 22;
private static String[] messages = {
"Channel binding mismatch", // BAD_BINDINGS
"Unsupported mechanism requested", // BAD_MECH
"Invalid name provided", // BAD_NAME
"Name of unsupported type provided", //BAD_NAMETYPE
"Invalid input status selector", // BAD_STATUS
"Token had invalid integrity check", // BAD_SIG
"Specified security context expired", // CONTEXT_EXPIRED
"Expired credentials detected", // CREDENTIALS_EXPIRED
"Defective credential detected", // DEFECTIVE_CREDENTIAL
"Defective token detected", // DEFECTIVE_TOKEN
"Failure unspecified at GSS-API level", // FAILURE
"Security context init/accept not yet called or context deleted",
// NO_CONTEXT
"No valid credentials provided", // NO_CRED
"Unsupported QOP value", // BAD_QOP
"Operation unauthorized", // UNAUTHORIZED
"Operation unavailable", // UNAVAILABLE
"Duplicate credential element requested", //DUPLICATE_ELEMENT
"Name contains multi-mechanism elements", // NAME_NOT_MN
"The token was a duplicate of an earlier token", //DUPLICATE_TOKEN
"The token's validity period has expired", //OLD_TOKEN
"A later token has already been processed", //UNSEQ_TOKEN
"An expected per-message token was not received", //GAP_TOKEN
};
/**
* The major code for this exception
*
* @serial
*/
private int major;
/**
* The minor code for this exception
*
* @serial
*/
private int minor = 0;
/**
* The text string for minor code
*
* @serial
*/
private String minorMessage = null;
/**
* Alternate text string for major code
*
* @serial
*/
private String majorString = null;
/**
* Creates a GSSException object with a specified major code.
*
* @param majorCode the The GSS error code for the problem causing this
* exception to be thrown.
*/
public GSSException (int majorCode) {
if (validateMajor(majorCode))
major = majorCode;
else
major = FAILURE;
}
/**
* Construct a GSSException object with a specified major code and a
* specific major string for it.
*
* @param majorCode the fatal error code causing this exception.
* @param majorString an expicit message to be included in this exception
*/
GSSException (int majorCode, String majorString) {
if (validateMajor(majorCode))
major = majorCode;
else
major = FAILURE;
this.majorString = majorString;
}
/**
* Creates a GSSException object with the specified major code, minor
* code, and minor code textual explanation. This constructor is to be
* used when the exception is originating from the underlying mechanism
* level. It allows the setting of both the GSS code and the mechanism
* code.
*
* @param majorCode the GSS error code for the problem causing this
* exception to be thrown.
* @param minorCode the mechanism level error code for the problem
* causing this exception to be thrown.
* @param minorString the textual explanation of the mechanism error
* code.
*/
public GSSException (int majorCode, int minorCode, String minorString) {
if (validateMajor(majorCode))
major = majorCode;
else
major = FAILURE;
minor = minorCode;
minorMessage = minorString;
}
/**
* Returns the GSS-API level major error code for the problem causing
* this exception to be thrown. Major error codes are
* defined at the mechanism independent GSS-API level in this
* class. Mechanism specific error codes that might provide more
* information are set as the minor error code.
*
* @return int the GSS-API level major error code causing this exception
* @see #getMajorString
* @see #getMinor
* @see #getMinorString
*/
public int getMajor() {
return major;
}
/**
* Returns the mechanism level error code for the problem causing this
* exception to be thrown. The minor code is set by the underlying
* mechanism.
*
* @return int the mechanism error code; 0 indicates that it has not
* been set.
* @see #getMinorString
* @see #setMinor
*/
public int getMinor(){
return minor;
}
/**
* Returns a string explaining the GSS-API level major error code in
* this exception.
*
* @return String explanation string for the major error code
* @see #getMajor
* @see #toString
*/
public String getMajorString() {
if (majorString != null)
return majorString;
else
return messages[major - 1];
}
/**
* Returns a string explaining the mechanism specific error code.
* If the minor status code is 0, then no mechanism level error details
* will be available.
*
* @return String a textual explanation of mechanism error code
* @see #getMinor
* @see #getMajorString
* @see #toString
*/
public String getMinorString() {
return minorMessage;
}
/**
* Used by the exception thrower to set the mechanism
* level minor error code and its string explanation. This is used by
* mechanism providers to indicate error details.
*
* @param minorCode the mechanism specific error code
* @param message textual explanation of the mechanism error code
* @see #getMinor
*/
public void setMinor(int minorCode, String message) {
minor = minorCode;
minorMessage = message;
}
/**
* Returns a textual representation of both the major and the minor
* status codes.
*
* @return a String with the error descriptions
*/
public String toString() {
return ("GSSException: " + getMessage());
}
/**
* Returns a textual representation of both the major and the minor
* status codes.
*
* @return a String with the error descriptions
*/
public String getMessage() {
if (minor == 0)
return (getMajorString());
return (getMajorString()
+ " (Mechanism level: " + getMinorString() + ")");
}
/*
* Validates the major code in the proper range.
*/
private boolean validateMajor(int major) {
if (major > 0 && major <= messages.length)
return (true);
return (false);
}
}

View file

@ -0,0 +1,729 @@
/*
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.ietf.jgss;
import java.security.Provider;
/**
* This class serves as a factory for other important
* GSS-API classes and also provides information about the mechanisms that
* are supported. It can create instances of classes
* implementing the following three GSS-API interfaces: {@link
* GSSName GSSName}, {@link GSSCredential GSSCredential}, and {@link
* GSSContext GSSContext}. It also has methods to query for the list
* of available mechanisms and the nametypes that each mechanism
* supports.<p>
*
* An instance of the default <code>GSSManager</code> subclass
* may be obtained through the static method {@link #getInstance()
* getInstance}, but applications are free to instantiate other subclasses
* of <code>GSSManager</code>. The default <code>GSSManager</code> instance
* will support the Kerberos v5 GSS-API mechanism in addition to any
* others. This mechanism is identified by the Oid "1.2.840.113554.1.2.2"
* and is defined in RFC 1964.<p>
*
* A subclass extending the <code>GSSManager</code> abstract class may be
* implemented as a modular provider based layer that utilizes some well
* known service provider specification. The <code>GSSManager</code> API
* allows the application to set provider preferences on
* such an implementation. These methods also allow the implementation to
* throw a well-defined exception in case provider based configuration is
* not supported. Applications that expect to be portable should be aware
* of this and recover cleanly by catching the exception.<p>
*
* It is envisioned that there will be three most common ways in which
* providers will be used:
* <ol>
* <li> The application does not care about what provider is used (the
* default case).
* <li> The application wants a particular provider to be used
* preferentially, either for a particular mechanism or all the
* time, irrespective of mechanism.
* <li> The application wants to use the locally configured providers
* as far as possible but if support is missing for one or more
* mechanisms then it wants to fall back on its own provider.
*</ol><p>
*
* The <code>GSSManager</code> class has two methods that enable these modes of
* usage: {@link #addProviderAtFront(Provider, Oid) addProviderAtFront} and
* {@link #addProviderAtEnd(Provider, Oid) addProviderAtEnd}. These methods
* have the effect of creating an ordered list of <i>&lt;provider,
* oid&gt;</i> pairs where each pair indicates a preference of provider
* for a given oid.<p>
*
* It is important to note that there are certain interactions
* between the different GSS-API objects that are created by a
* GSSManager, where the provider that is used for a particular mechanism
* might need to be consistent across all objects. For instance, if a
* GSSCredential contains elements from a provider <i>p</i> for a mechanism
* <i>m</i>, it should generally be passed in to a GSSContext that will use
* provider <i>p</i> for the mechanism <i>m</i>. A simple rule of thumb
* that will maximize portability is that objects created from different
* GSSManager's should not be mixed, and if possible, a different
* GSSManager instance should be created if the application wants to invoke
* the <code>addProviderAtFront</code> method on a GSSManager that has
* already created an object.<p>
*
* Here is some sample code showing how the GSSManager might be used:
* <pre>
* GSSManager manager = GSSManager.getInstance();
*
* Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2");
* Oid krb5PrincipalNameType = new Oid("1.2.840.113554.1.2.2.1");
*
* // Identify who the client wishes to be
* GSSName userName = manager.createName("duke", GSSName.NT_USER_NAME);
*
* // Identify the name of the server. This uses a Kerberos specific
* // name format.
* GSSName serverName = manager.createName("nfs/foo.sun.com",
* krb5PrincipalNameType);
*
* // Acquire credentials for the user
* GSSCredential userCreds = manager.createCredential(userName,
* GSSCredential.DEFAULT_LIFETIME,
* krb5Mechanism,
* GSSCredential.INITIATE_ONLY);
*
* // Instantiate and initialize a security context that will be
* // established with the server
* GSSContext context = manager.createContext(serverName,
* krb5Mechanism,
* userCreds,
* GSSContext.DEFAULT_LIFETIME);
* </pre><p>
*
* The server side might use the following variation of this source:
*
* <pre>
* // Acquire credentials for the server
* GSSCredential serverCreds = manager.createCredential(serverName,
* GSSCredential.DEFAULT_LIFETIME,
* krb5Mechanism,
* GSSCredential.ACCEPT_ONLY);
*
* // Instantiate and initialize a security context that will
* // wait for an establishment request token from the client
* GSSContext context = manager.createContext(serverCreds);
* </pre>
*
* @author Mayank Upadhyay
* @see GSSName
* @see GSSCredential
* @see GSSContext
* @since 1.4
*/
public abstract class GSSManager {
/**
* Returns the default GSSManager implementation.
*
* @return a GSSManager implementation
*/
public static GSSManager getInstance() {
return new sun.security.jgss.GSSManagerImpl();
}
/**
* Returns a list of mechanisms that are available to GSS-API callers
* through this GSSManager. The default GSSManager obtained from the
* {@link #getInstance() getInstance()} method includes the Oid
* "1.2.840.113554.1.2.2" in its list. This Oid identifies the Kerberos
* v5 GSS-API mechanism that is defined in RFC 1964.
*
* @return an array of Oid objects corresponding to the mechanisms that
* are available. A <code>null</code> value is returned when no
* mechanism are available (an example of this would be when mechanism
* are dynamically configured, and currently no mechanisms are
* installed).
*/
public abstract Oid[] getMechs();
/**
* Returns then name types supported by the indicated mechanism.<p>
*
* The default GSSManager instance includes support for the Kerberos v5
* mechanism. When this mechanism ("1.2.840.113554.1.2.2") is indicated,
* the returned list will contain at least the following nametypes:
* {@link GSSName#NT_HOSTBASED_SERVICE GSSName.NT_HOSTBASED_SERVICE},
* {@link GSSName#NT_EXPORT_NAME GSSName.NT_EXPORT_NAME}, and the
* Kerberos v5 specific Oid "1.2.840.113554.1.2.2.1". The namespace for
* the Oid "1.2.840.113554.1.2.2.1" is defined in RFC 1964.
*
* @return an array of Oid objects corresponding to the name types that
* the mechanism supports.
* @param mech the Oid of the mechanism to query
*
* @see #getMechsForName(Oid)
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#BAD_MECH GSSException.BAD_MECH}
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public abstract Oid[] getNamesForMech(Oid mech)
throws GSSException;
/**
* Returns a list of mechanisms that support the indicated name type.<p>
*
* The Kerberos v5 mechanism ("1.2.840.113554.1.2.2") will always be
* returned in this list when the indicated nametype is one of
* {@link GSSName#NT_HOSTBASED_SERVICE GSSName.NT_HOSTBASED_SERVICE},
* {@link GSSName#NT_EXPORT_NAME GSSName.NT_EXPORT_NAME}, or
* "1.2.840.113554.1.2.2.1".
*
* @return an array of Oid objects corresponding to the mechanisms that
* support the specified name type. <code>null</code> is returned when no
* mechanisms are found to support the specified name type.
* @param nameType the Oid of the name type to look for
*
* @see #getNamesForMech(Oid)
*/
public abstract Oid[] getMechsForName(Oid nameType);
/**
* Factory method to convert a string name from the
* specified namespace to a GSSName object. In general, the
* <code>GSSName</code> object created will contain multiple
* representations of the name, one for each mechanism that is
* supported; two examples that are exceptions to this are when
* the namespace type parameter indicates NT_EXPORT_NAME or when the
* GSS-API implementation is not multi-mechanism. It is
* not recommended to use this method with a NT_EXPORT_NAME type because
* representing a previously exported name consisting of arbitrary bytes
* as a String might cause problems with character encoding schemes. In
* such cases it is recommended that the bytes be passed in directly to
* the overloaded form of this method {@link #createName(byte[],
* Oid) createName}.
*
* @param nameStr the string representing a printable form of the name to
* create.
* @param nameType the Oid specifying the namespace of the printable name
* supplied. <code>null</code> can be used to specify
* that a mechanism specific default printable syntax should
* be assumed by each mechanism that examines nameStr.
* It is not advisable to use the nametype NT_EXPORT_NAME with this
* method.
* @return a GSSName representing the indicated principal
*
* @see GSSName
* @see GSSName#NT_EXPORT_NAME
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#BAD_NAMETYPE GSSException.BAD_NAMETYPE},
* {@link GSSException#BAD_NAME GSSException.BAD_NAME},
* {@link GSSException#BAD_MECH GSSException.BAD_MECH},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public abstract GSSName createName(String nameStr, Oid nameType)
throws GSSException;
/**
* Factory method to convert a byte array containing a
* name from the specified namespace to a GSSName object. In general,
* the <code>GSSName</code> object created will contain multiple
* representations of the name, one for each mechanism that is
* supported; two examples that are exceptions to this are when the
* namespace type parameter indicates NT_EXPORT_NAME or when the
* GSS-API implementation is not multi-mechanism. The bytes that are
* passed in are interpreted by each underlying mechanism according to
* some encoding scheme of its choice for the given nametype.
*
* @param name the byte array containing the name to create
* @param nameType the Oid specifying the namespace of the name supplied
* in the byte array. <code>null</code> can be used to specify that a
* mechanism specific default syntax should be assumed by each mechanism
* that examines the byte array.
* @return a GSSName representing the indicated principal
*
* @see GSSName
* @see GSSName#NT_EXPORT_NAME
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#BAD_NAMETYPE GSSException.BAD_NAMETYPE},
* {@link GSSException#BAD_NAME GSSException.BAD_NAME},
* {@link GSSException#BAD_MECH GSSException.BAD_MECH},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public abstract GSSName createName(byte name[], Oid nameType)
throws GSSException;
/**
* Factory method to convert a string name from the
* specified namespace to a GSSName object and canonicalize it at the
* same time for a mechanism. In other words, this method is
* a utility that does the equivalent of two steps: the {@link
* #createName(String, Oid) createName} and then also the {@link
* GSSName#canonicalize(Oid) GSSName.canonicalize}.
*
* @param nameStr the string representing a printable form of the name to
* create.
* @param nameType the Oid specifying the namespace of the printable name
* supplied. <code>null</code> can be used to specify
* that a mechanism specific default printable syntax should
* be assumed by each mechanism that examines nameStr.
* It is not advisable to use the nametype NT_EXPORT_NAME with this
* method.
* @param mech Oid specifying the mechanism for which the name should be
* canonicalized
* @return a GSSName representing the indicated principal
*
* @see GSSName#canonicalize(Oid)
* @see GSSName#NT_EXPORT_NAME
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#BAD_NAMETYPE GSSException.BAD_NAMETYPE},
* {@link GSSException#BAD_NAME GSSException.BAD_NAME},
* {@link GSSException#BAD_MECH GSSException.BAD_MECH},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public abstract GSSName createName(String nameStr, Oid nameType,
Oid mech) throws GSSException;
/**
* Factory method to convert a byte array containing a
* name from the specified namespace to a GSSName object and canonicalize
* it at the same time for a mechanism. In other words, this method is a
* utility that does the equivalent of two steps: the {@link
* #createName(byte[], Oid) createName} and then also {@link
* GSSName#canonicalize(Oid) GSSName.canonicalize}.
*
* @param name the byte array containing the name to create
* @param nameType the Oid specifying the namespace of the name supplied
* in the byte array. <code>null</code> can be used to specify that a
* mechanism specific default syntax should be assumed by each mechanism
* that examines the byte array.
* @param mech Oid specifying the mechanism for which the name should be
* canonicalized
* @return a GSSName representing the indicated principal
*
* @see GSSName#canonicalize(Oid)
* @see GSSName#NT_EXPORT_NAME
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#BAD_NAMETYPE GSSException.BAD_NAMETYPE},
* {@link GSSException#BAD_NAME GSSException.BAD_NAME},
* {@link GSSException#BAD_MECH GSSException.BAD_MECH},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public abstract GSSName createName(byte name[], Oid nameType, Oid mech)
throws GSSException;
/**
* Factory method for acquiring default credentials. This will cause
* the GSS-API to use system specific defaults for the set of mechanisms,
* name, and lifetime.<p>
*
* GSS-API mechanism providers must impose a local access-control
* policy on callers to prevent unauthorized callers from acquiring
* credentials to which they are not entitled. The kinds of permissions
* needed by different mechanism providers will be documented on a
* per-mechanism basis. A failed permission check might cause a {@link
* java.lang.SecurityException SecurityException} to be thrown from
* this method.
*
* @param usage The intended usage for this credential object. The value
* of this parameter must be one of:
* {@link GSSCredential#INITIATE_AND_ACCEPT
* GSSCredential.INITIATE_AND_ACCEPT},
* {@link GSSCredential#ACCEPT_ONLY GSSCredential.ACCEPT_ONLY}, and
* {@link GSSCredential#INITIATE_ONLY GSSCredential.INITIATE_ONLY}.
* @return a GSSCredential of the requested type.
*
* @see GSSCredential
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#BAD_MECH GSSException.BAD_MECH},
* {@link GSSException#BAD_NAMETYPE GSSException.BAD_NAMETYPE},
* {@link GSSException#BAD_NAME GSSException.BAD_NAME},
* {@link GSSException#CREDENTIALS_EXPIRED
* GSSException.CREDENTIALS_EXPIRED},
* {@link GSSException#NO_CRED GSSException.NO_CRED},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public abstract GSSCredential createCredential (int usage)
throws GSSException;
/**
* Factory method for acquiring a single mechanism credential.<p>
*
* GSS-API mechanism providers must impose a local access-control
* policy on callers to prevent unauthorized callers from acquiring
* credentials to which they are not entitled. The kinds of permissions
* needed by different mechanism providers will be documented on a
* per-mechanism basis. A failed permission check might cause a {@link
* java.lang.SecurityException SecurityException} to be thrown from
* this method. <p>
*
* Non-default values for lifetime cannot always be honored by the
* underlying mechanisms, thus applications should be prepared to call
* {@link GSSCredential#getRemainingLifetime() getRemainingLifetime}
* on the returned credential.
*
* @param name the name of the principal for whom this credential is to be
* acquired. Use <code>null</code> to specify the default principal.
* @param lifetime The number of seconds that credentials should remain
* valid. Use {@link GSSCredential#INDEFINITE_LIFETIME
* GSSCredential.INDEFINITE_LIFETIME} to request that the credentials
* have the maximum permitted lifetime. Use {@link
* GSSCredential#DEFAULT_LIFETIME GSSCredential.DEFAULT_LIFETIME} to
* request default credential lifetime.
* @param mech the Oid of the desired mechanism. Use <code>(Oid) null
* </code> to request the default mechanism.
* @param usage The intended usage for this credential object. The value
* of this parameter must be one of:
* {@link GSSCredential#INITIATE_AND_ACCEPT
* GSSCredential.INITIATE_AND_ACCEPT},
* {@link GSSCredential#ACCEPT_ONLY GSSCredential.ACCEPT_ONLY}, and
* {@link GSSCredential#INITIATE_ONLY GSSCredential.INITIATE_ONLY}.
* @return a GSSCredential of the requested type.
*
* @see GSSCredential
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#BAD_MECH GSSException.BAD_MECH},
* {@link GSSException#BAD_NAMETYPE GSSException.BAD_NAMETYPE},
* {@link GSSException#BAD_NAME GSSException.BAD_NAME},
* {@link GSSException#CREDENTIALS_EXPIRED
* GSSException.CREDENTIALS_EXPIRED},
* {@link GSSException#NO_CRED GSSException.NO_CRED},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public abstract GSSCredential createCredential (GSSName name,
int lifetime, Oid mech, int usage)
throws GSSException;
/**
* Factory method for acquiring credentials over a set of
* mechanisms. This method attempts to acquire credentials for
* each of the mechanisms specified in the array called mechs. To
* determine the list of mechanisms for which the acquisition of
* credentials succeeded, the caller should use the {@link
* GSSCredential#getMechs() GSSCredential.getMechs} method.<p>
*
* GSS-API mechanism providers must impose a local access-control
* policy on callers to prevent unauthorized callers from acquiring
* credentials to which they are not entitled. The kinds of permissions
* needed by different mechanism providers will be documented on a
* per-mechanism basis. A failed permission check might cause a {@link
* java.lang.SecurityException SecurityException} to be thrown from
* this method.<p>
*
* Non-default values for lifetime cannot always be honored by the
* underlying mechanisms, thus applications should be prepared to call
* {@link GSSCredential#getRemainingLifetime() getRemainingLifetime}
* on the returned credential.
*
* @param name the name of the principal for whom this credential is to
* be acquired. Use <code>null</code> to specify the default
* principal.
* @param lifetime The number of seconds that credentials should remain
* valid. Use {@link GSSCredential#INDEFINITE_LIFETIME
* GSSCredential.INDEFINITE_LIFETIME} to request that the credentials
* have the maximum permitted lifetime. Use {@link
* GSSCredential#DEFAULT_LIFETIME GSSCredential.DEFAULT_LIFETIME} to
* request default credential lifetime.
* @param mechs an array of Oid's indicating the mechanisms over which
* the credential is to be acquired. Use <code>(Oid[]) null</code> for
* requesting a system specific default set of mechanisms.
* @param usage The intended usage for this credential object. The value
* of this parameter must be one of:
* {@link GSSCredential#INITIATE_AND_ACCEPT
* GSSCredential.INITIATE_AND_ACCEPT},
* {@link GSSCredential#ACCEPT_ONLY GSSCredential.ACCEPT_ONLY}, and
* {@link GSSCredential#INITIATE_ONLY GSSCredential.INITIATE_ONLY}.
* @return a GSSCredential of the requested type.
*
* @see GSSCredential
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#BAD_MECH GSSException.BAD_MECH},
* {@link GSSException#BAD_NAMETYPE GSSException.BAD_NAMETYPE},
* {@link GSSException#BAD_NAME GSSException.BAD_NAME},
* {@link GSSException#CREDENTIALS_EXPIRED
* GSSException.CREDENTIALS_EXPIRED},
* {@link GSSException#NO_CRED GSSException.NO_CRED},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public abstract GSSCredential createCredential(GSSName name,
int lifetime, Oid mechs[], int usage)
throws GSSException;
/**
* Factory method for creating a context on the initiator's
* side.
*
* Some mechanism providers might require that the caller be granted
* permission to initiate a security context. A failed permission check
* might cause a {@link java.lang.SecurityException SecurityException}
* to be thrown from this method.<p>
*
* Non-default values for lifetime cannot always be honored by the
* underlying mechanism, thus applications should be prepared to call
* {@link GSSContext#getLifetime() getLifetime} on the returned
* context.
*
* @param peer the name of the target peer.
* @param mech the Oid of the desired mechanism. Use <code>null</code>
* to request the default mechanism.
* @param myCred the credentials of the initiator. Use
* <code>null</code> to act as the default initiator principal.
* @param lifetime the lifetime, in seconds, requested for the
* context. Use {@link GSSContext#INDEFINITE_LIFETIME
* GSSContext.INDEFINITE_LIFETIME} to request that the context have the
* maximum permitted lifetime. Use {@link GSSContext#DEFAULT_LIFETIME
* GSSContext.DEFAULT_LIFETIME} to request a default lifetime for the
* context.
* @return an unestablished GSSContext
*
* @see GSSContext
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#NO_CRED GSSException.NO_CRED}
* {@link GSSException#CREDENTIALS_EXPIRED
* GSSException.CREDENTIALS_EXPIRED}
* {@link GSSException#BAD_NAMETYPE GSSException.BAD_NAMETYPE}
* {@link GSSException#BAD_MECH GSSException.BAD_MECH}
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public abstract GSSContext createContext(GSSName peer, Oid mech,
GSSCredential myCred, int lifetime)
throws GSSException;
/**
* Factory method for creating a context on the acceptor' side. The
* context's properties will be determined from the input token supplied
* to the accept method.
*
* Some mechanism providers might require that the caller be granted
* permission to accept a security context. A failed permission check
* might cause a {@link java.lang.SecurityException SecurityException}
* to be thrown from this method.
*
* @param myCred the credentials for the acceptor. Use
* <code>null</code> to act as a default acceptor principal.
* @return an unestablished GSSContext
*
* @see GSSContext
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#NO_CRED GSSException.NO_CRED}
* {@link GSSException#CREDENTIALS_EXPIRED
* GSSException.CREDENTIALS_EXPIRED}
* {@link GSSException#BAD_MECH GSSException.BAD_MECH}
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public abstract GSSContext createContext(GSSCredential myCred)
throws GSSException;
/**
* Factory method for creating a previously exported context. The
* context properties will be determined from the input token and
* cannot be modified through the set methods.<p>
*
* Implementations are not required to support the inter-process
* transfer of security contexts. Before exporting a context, calling
* the {@link GSSContext#isTransferable() GSSContext.isTransferable}
* will indicate if the context is transferable. Calling this method in
* an implementation that does not support it will result in a
* <code>GSSException</code> with the error
* code {@link GSSException#UNAVAILABLE GSSException.UNAVAILABLE}.
*
* Some mechanism providers might require that the caller be granted
* permission to initiate or accept a security context. A failed
* permission check might cause a {@link java.lang.SecurityException
* SecurityException} to be thrown from this method.
*
* @param interProcessToken the token previously emitted from the
* export method.
* @return the previously established GSSContext
*
* @see GSSContext
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#NO_CONTEXT GSSException.NO_CONTEXT},
* {@link GSSException#DEFECTIVE_TOKEN GSSException.DEFECTIVE_TOKEN},
* {@link GSSException#UNAVAILABLE GSSException.UNAVAILABLE},
* {@link GSSException#UNAUTHORIZED GSSException.UNAUTHORIZED},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public abstract GSSContext createContext(byte [] interProcessToken)
throws GSSException;
/**
* This method is used to indicate to the GSSManager that the
* application would like a particular provider to be used ahead of all
* others when support is desired for the given mechanism. When a value
* of null is used instead of an <code>Oid</code> for the mechanism,
* the GSSManager must use the indicated provider ahead of all others
* no matter what the mechanism is. Only when the indicated provider
* does not support the needed mechanism should the GSSManager move on
* to a different provider.<p>
*
* Calling this method repeatedly preserves the older settings but
* lowers them in preference thus forming an ordered list of provider
* and <code>Oid</code> pairs that grows at the top.<p>
*
* Calling addProviderAtFront with a null <code>Oid</code> will remove
* all previous preferences that were set for this provider in the
* GSSManager instance. Calling addProviderAtFront with a non-null
* <code>Oid</code> will remove any previous preference that was set
* using this mechanism and this provider together.<p>
*
* If the GSSManager implementation does not support an SPI with a
* pluggable provider architecture it should throw a GSSException with
* the status code GSSException.UNAVAILABLE to indicate that the
* operation is unavailable.<p>
*
* Suppose an application desired that the provider A always be checked
* first when any mechanism is needed, it would call:
* <pre>
* GSSManager mgr = GSSManager.getInstance();
* // mgr may at this point have its own pre-configured list
* // of provider preferences. The following will prepend to
* // any such list:
*
* mgr.addProviderAtFront(A, null);
* </pre>
* Now if it also desired that the mechanism of Oid m1 always be
* obtained from the provider B before the previously set A was checked,
* it would call:
* <pre>
* mgr.addProviderAtFront(B, m1);
* </pre>
* The GSSManager would then first check with B if m1 was needed. In
* case B did not provide support for m1, the GSSManager would continue
* on to check with A. If any mechanism m2 is needed where m2 is
* different from m1 then the GSSManager would skip B and check with A
* directly.<p>
*
* Suppose at a later time the following call is made to the same
* GSSManager instance:
* <pre>
* mgr.addProviderAtFront(B, null)
* </pre>
* then the previous setting with the pair (B, m1) is subsumed by this
* and should be removed. Effectively the list of preferences now
* becomes {(B, null), (A, null),
* ... //followed by the pre-configured list.<p>
*
* Please note, however, that the following call:
* <pre>
* mgr.addProviderAtFront(A, m3)
* </pre>
* does not subsume the previous setting of (A, null) and the list will
* effectively become {(A, m3), (B, null), (A, null), ...}
*
* @param p the provider instance that should be used whenever support
* is needed for mech.
* @param mech the mechanism for which the provider is being set
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#UNAVAILABLE GSSException.UNAVAILABLE},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public abstract void addProviderAtFront(Provider p, Oid mech)
throws GSSException;
/**
* This method is used to indicate to the GSSManager that the
* application would like a particular provider to be used if no other
* provider can be found that supports the given mechanism. When a value
* of null is used instead of an Oid for the mechanism, the GSSManager
* must use the indicated provider for any mechanism.<p>
*
* Calling this method repeatedly preserves the older settings but
* raises them above newer ones in preference thus forming an ordered
* list of providers and Oid pairs that grows at the bottom. Thus the
* older provider settings will be utilized first before this one is.<p>
*
* If there are any previously existing preferences that conflict with
* the preference being set here, then the GSSManager should ignore this
* request.<p>
*
* If the GSSManager implementation does not support an SPI with a
* pluggable provider architecture it should throw a GSSException with
* the status code GSSException.UNAVAILABLE to indicate that the
* operation is unavailable.<p>
*
* Suppose an application desired that when a mechanism of Oid m1 is
* needed the system default providers always be checked first, and only
* when they do not support m1 should a provider A be checked. It would
* then make the call:
* <pre>
* GSSManager mgr = GSSManager.getInstance();
* mgr.addProviderAtEnd(A, m1);
* </pre>
* Now, if it also desired that for all mechanisms the provider B be
* checked after all configured providers have been checked, it would
* then call:
* <pre>
* mgr.addProviderAtEnd(B, null);
* </pre>
* Effectively the list of preferences now becomes {..., (A, m1), (B,
* null)}.<p>
*
* Suppose at a later time the following call is made to the same
* GSSManager instance:
* <pre>
* mgr.addProviderAtEnd(B, m2)
* </pre>
* then the previous setting with the pair (B, null) subsumes this and
* therefore this request should be ignored. The same would happen if a
* request is made for the already existing pairs of (A, m1) or (B,
* null).<p>
*
* Please note, however, that the following call:
* <pre>
* mgr.addProviderAtEnd(A, null)
* </pre>
* is not subsumed by the previous setting of (A, m1) and the list will
* effectively become {..., (A, m1), (B, null), (A, null)}
*
* @param p the provider instance that should be used whenever support
* is needed for mech.
* @param mech the mechanism for which the provider is being set
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#UNAVAILABLE GSSException.UNAVAILABLE},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public abstract void addProviderAtEnd(Provider p, Oid mech)
throws GSSException;
}

View file

@ -0,0 +1,304 @@
/*
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.ietf.jgss;
/**
* This interface encapsulates a single GSS-API principal entity. The
* application obtains an implementation of this interface
* through one of the <code>createName</code> methods that exist in the {@link
* GSSManager GSSManager} class. Conceptually a GSSName contains many
* representations of the entity or many primitive name elements, one for
* each supported underlying mechanism. In GSS terminology, a GSSName that
* contains an element from just one mechanism is called a Mechanism Name
* (MN)<p>
*
* Since different authentication mechanisms may employ different
* namespaces for identifying their principals, GSS-API's naming support is
* necessarily complex in multi-mechanism environments (or even in some
* single-mechanism environments where the underlying mechanism supports
* multiple namespaces). Different name formats and their definitions are
* identified with {@link Oid Oid's} and some standard types
* are defined in this interface. The format of the names can be derived
* based on the unique <code>Oid</code> of its name type.<p>
*
* Included below are code examples utilizing the <code>GSSName</code> interface.
* The code below creates a <code>GSSName</code>, converts it to an MN, performs a
* comparison, obtains a printable representation of the name, exports it
* to a byte array and then re-imports to obtain a
* new <code>GSSName</code>.
* <pre>
* GSSManager manager = GSSManager.getInstance();
*
* // create a host based service name
* GSSName name = manager.createName("service@host",
* GSSName.NT_HOSTBASED_SERVICE);
*
* Oid krb5 = new Oid("1.2.840.113554.1.2.2");
*
* GSSName mechName = name.canonicalize(krb5);
*
* // the above two steps are equivalent to the following
* GSSName mechName = manager.createName("service@host",
* GSSName.NT_HOSTBASED_SERVICE, krb5);
*
* // perform name comparison
* if (name.equals(mechName))
* print("Names are equals.");
*
* // obtain textual representation of name and its printable
* // name type
* print(mechName.toString() +
* mechName.getStringNameType().toString());
*
* // export and re-import the name
* byte [] exportName = mechName.export();
*
* // create a new name object from the exported buffer
* GSSName newName = manager.createName(exportName,
* GSSName.NT_EXPORT_NAME);
*
* </pre>
* If a security manager is installed, in order to create a {@code GSSName}
* that contains a Kerberos name element without providing its realm,
* a {@link javax.security.auth.kerberos.ServicePermission ServicePermission}
* must be granted and the service principal of the permission must minimally
* be inside the Kerberos name element's realm. For example, if the result of
* {@link GSSManager#createName(String, Oid) createName("user", NT_USER_NAME)}
* contains a Kerberos name element {@code user@EXAMPLE.COM}, then
* a {@code ServicePermission} with service principal
* {@code host/www.example.com@EXAMPLE.COM} (and any action) must be granted.
* Otherwise, the creation will throw a {@link GSSException} containing the
* {@code GSSException.FAILURE} error code.
*
* @see #export()
* @see #equals(GSSName)
* @see GSSManager#createName(String, Oid)
* @see GSSManager#createName(String, Oid, Oid)
* @see GSSManager#createName(byte[], Oid)
*
* @author Mayank Upadhyay
* @since 1.4
*/
public interface GSSName {
/**
* Oid indicating a host-based service name form. It is used to
* represent services associated with host computers. This name form
* is constructed using two elements, "service" and "hostname", as
* follows: service@hostname.<p>
*
* It represents the following Oid value:<br>
* <code>{ iso(1) member-body(2) United
* States(840) mit(113554) infosys(1) gssapi(2) generic(1) service_name(4)
* }</code>
*/
public static final Oid NT_HOSTBASED_SERVICE
= Oid.getInstance("1.2.840.113554.1.2.1.4");
/**
* Name type to indicate a named user on a local system.<p>
* It represents the following Oid value:<br>
* <code>{ iso(1) member-body(2) United
* States(840) mit(113554) infosys(1) gssapi(2) generic(1) user_name(1)
* }</code>
*/
public static final Oid NT_USER_NAME
= Oid.getInstance("1.2.840.113554.1.2.1.1");
/**
* Name type to indicate a numeric user identifier corresponding to a
* user on a local system. (e.g. Uid).<p>
*
* It represents the following Oid value:<br>
* <code>{ iso(1) member-body(2) United States(840) mit(113554)
* infosys(1) gssapi(2) generic(1) machine_uid_name(2) }</code>
*/
public static final Oid NT_MACHINE_UID_NAME
= Oid.getInstance("1.2.840.113554.1.2.1.2");
/**
* Name type to indicate a string of digits representing the numeric
* user identifier of a user on a local system.<p>
*
* It represents the following Oid value:<br>
* <code>{ iso(1) member-body(2) United
* States(840) mit(113554) infosys(1) gssapi(2) generic(1)
* string_uid_name(3) }</code>
*/
public static final Oid NT_STRING_UID_NAME
= Oid.getInstance("1.2.840.113554.1.2.1.3");
/**
* Name type for representing an anonymous entity.<p>
* It represents the following Oid value:<br>
* <code>{ 1(iso), 3(org), 6(dod), 1(internet),
* 5(security), 6(nametypes), 3(gss-anonymous-name) }</code>
*/
public static final Oid NT_ANONYMOUS
= Oid.getInstance("1.3.6.1.5.6.3");
/**
* Name type used to indicate an exported name produced by the export
* method.<p>
*
* It represents the following Oid value:<br> <code>{ 1(iso),
* 3(org), 6(dod), 1(internet), 5(security), 6(nametypes),
* 4(gss-api-exported-name) }</code>
*/
public static final Oid NT_EXPORT_NAME
= Oid.getInstance("1.3.6.1.5.6.4");
/**
* Compares two <code>GSSName</code> objects to determine if they refer to the
* same entity.
*
* @param another the <code>GSSName</code> to compare this name with
* @return true if the two names contain at least one primitive element
* in common. If either of the names represents an anonymous entity, the
* method will return false.
*
* @throws GSSException when the names cannot be compared, containing the following
* major error codes:
* {@link GSSException#BAD_NAMETYPE GSSException.BAD_NAMETYPE},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public boolean equals(GSSName another) throws GSSException;
/**
* Compares this <code>GSSName</code> object to another Object that might be a
* <code>GSSName</code>. The behaviour is exactly the same as in {@link
* #equals(GSSName) equals} except that no GSSException is thrown;
* instead, false will be returned in the situation where an error
* occurs.
* @return true if the object to compare to is also a <code>GSSName</code> and the two
* names refer to the same entity.
* @param another the object to compare this name to
* @see #equals(GSSName)
*/
public boolean equals(Object another);
/**
* Returns a hashcode value for this GSSName.
*
* @return a hashCode value
*/
public int hashCode();
/**
* Creates a name that is canonicalized for some
* mechanism.
*
* @return a <code>GSSName</code> that contains just one primitive
* element representing this name in a canonicalized form for the desired
* mechanism.
* @param mech the oid for the mechanism for which the canonical form of
* the name is requested.
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#BAD_MECH GSSException.BAD_MECH},
* {@link GSSException#BAD_NAMETYPE GSSException.BAD_NAMETYPE},
* {@link GSSException#BAD_NAME GSSException.BAD_NAME},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public GSSName canonicalize(Oid mech) throws GSSException;
/**
* Returns a canonical contiguous byte representation of a mechanism name
* (MN), suitable for direct, byte by byte comparison by authorization
* functions. If the name is not an MN, implementations may throw a
* GSSException with the NAME_NOT_MN status code. If an implementation
* chooses not to throw an exception, it should use some system specific
* default mechanism to canonicalize the name and then export
* it. Structurally, an exported name object consists of a header
* containing an OID identifying the mechanism that authenticated the
* name, and a trailer containing the name itself, where the syntax of
* the trailer is defined by the individual mechanism specification. The
* format of the header of the output buffer is specified in RFC 2743.<p>
*
* The exported name is useful when used in large access control lists
* where the overhead of creating a <code>GSSName</code> object on each
* name and invoking the equals method on each name from the ACL may be
* prohibitive.<p>
*
* Exported names may be re-imported by using the byte array factory
* method {@link GSSManager#createName(byte[], Oid)
* GSSManager.createName} and specifying the NT_EXPORT_NAME as the name
* type object identifier. The resulting <code>GSSName</code> name will
* also be a MN.
*
* @return a byte[] containing the exported name. RFC 2743 defines the
* "Mechanism-Independent Exported Name Object Format" for these bytes.
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#BAD_NAME GSSException.BAD_NAME},
* {@link GSSException#BAD_NAMETYPE GSSException.BAD_NAMETYPE},
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public byte[] export() throws GSSException;
/**
* Returns a textual representation of the <code>GSSName</code> object. To retrieve
* the printed name format, which determines the syntax of the returned
* string, use the {@link #getStringNameType() getStringNameType}
* method.
*
* @return a String representing this name in printable form.
*/
public String toString();
/**
* Returns the name type of the printable
* representation of this name that can be obtained from the <code>
* toString</code> method.
*
* @return an Oid representing the namespace of the name returned
* from the toString method.
*
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public Oid getStringNameType() throws GSSException;
/**
* Tests if this name object represents an anonymous entity.
*
* @return true if this is an anonymous name, false otherwise.
*/
public boolean isAnonymous();
/**
* Tests if this name object represents a Mechanism Name (MN). An MN is
* a GSSName the contains exactly one mechanism's primitive name
* element.
*
* @return true if this is an MN, false otherwise.
*/
public boolean isMN();
}

View file

@ -0,0 +1,235 @@
/*
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.ietf.jgss;
/**
* This is a utility class used within the per-message GSSContext
* methods to convey per-message properties.<p>
*
* When used with the GSSContext interface's wrap and getMIC methods, an
* instance of this class is used to indicate the desired
* Quality-of-Protection (QOP) and to request if confidentiality services
* are to be applied to caller supplied data (wrap only). To request
* default QOP, the value of 0 should be used for QOP.<p>
*
* When used with the unwrap and verifyMIC methods of the GSSContext
* interface, an instance of this class will be used to indicate the
* applied QOP and confidentiality services over the supplied message.
* In the case of verifyMIC, the confidentiality state will always be
* <code>false</code>. Upon return from these methods, this object will also
* contain any supplementary status values applicable to the processed
* token. The supplementary status values can indicate old tokens, out
* of sequence tokens, gap tokens or duplicate tokens.
*
* @see GSSContext#wrap
* @see GSSContext#unwrap
* @see GSSContext#getMIC
* @see GSSContext#verifyMIC
*
* @author Mayank Upadhyay
* @since 1.4
*/
public class MessageProp {
private boolean privacyState;
private int qop;
private boolean dupToken;
private boolean oldToken;
private boolean unseqToken;
private boolean gapToken;
private int minorStatus;
private String minorString;
/**
* Constructor which sets the desired privacy state. The QOP value used
* is 0.
*
* @param privState the privacy (i.e. confidentiality) state
*/
public MessageProp(boolean privState) {
this(0, privState);
}
/**
* Constructor which sets the values for the qop and privacy state.
*
* @param qop the QOP value
* @param privState the privacy (i.e. confidentiality) state
*/
public MessageProp(int qop, boolean privState) {
this.qop = qop;
this.privacyState = privState;
resetStatusValues();
}
/**
* Retrieves the QOP value.
*
* @return an int representing the QOP value
* @see #setQOP
*/
public int getQOP() {
return qop;
}
/**
* Retrieves the privacy state.
*
* @return true if the privacy (i.e., confidentiality) state is true,
* false otherwise.
* @see #setPrivacy
*/
public boolean getPrivacy() {
return (privacyState);
}
/**
* Sets the QOP value.
*
* @param qop the int value to set the QOP to
* @see #getQOP
*/
public void setQOP(int qop) {
this.qop = qop;
}
/**
* Sets the privacy state.
*
* @param privState true is the privacy (i.e., confidentiality) state
* is true, false otherwise.
* @see #getPrivacy
*/
public void setPrivacy(boolean privState) {
this.privacyState = privState;
}
/**
* Tests if this is a duplicate of an earlier token.
*
* @return true if this is a duplicate, false otherwise.
*/
public boolean isDuplicateToken() {
return dupToken;
}
/**
* Tests if this token's validity period has expired, i.e., the token
* is too old to be checked for duplication.
*
* @return true if the token's validity period has expired, false
* otherwise.
*/
public boolean isOldToken() {
return oldToken;
}
/**
* Tests if a later token had already been processed.
*
* @return true if a later token had already been processed, false otherwise.
*/
public boolean isUnseqToken() {
return unseqToken;
}
/**
* Tests if an expected token was not received, i.e., one or more
* predecessor tokens have not yet been successfully processed.
*
* @return true if an expected per-message token was not received,
* false otherwise.
*/
public boolean isGapToken() {
return gapToken;
}
/**
* Retrieves the minor status code that the underlying mechanism might
* have set for this per-message operation.
*
* @return the int minor status
*/
public int getMinorStatus(){
return minorStatus;
}
/**
* Retrieves a string explaining the minor status code.
*
* @return a String corresponding to the minor status
* code. <code>null</code> will be returned when no minor status code
* has been set.
*/
public String getMinorString(){
return minorString;
}
/**
* This method sets the state for the supplementary information flags
* and the minor status in MessageProp. It is not used by the
* application but by the GSS implementation to return this information
* to the caller of a per-message context method.
*
* @param duplicate true if the token was a duplicate of an earlier
* token, false otherwise
* @param old true if the token's validity period has expired, false
* otherwise
* @param unseq true if a later token has already been processed, false
* otherwise
* @param gap true if one or more predecessor tokens have not yet been
* successfully processed, false otherwise
* @param minorStatus the int minor status code for the per-message
* operation
* @param minorString the textual representation of the minorStatus value
*/
public void setSupplementaryStates(boolean duplicate,
boolean old, boolean unseq, boolean gap,
int minorStatus, String minorString) {
this.dupToken = duplicate;
this.oldToken = old;
this.unseqToken = unseq;
this.gapToken = gap;
this.minorStatus = minorStatus;
this.minorString = minorString;
}
/**
* Resets the supplementary status values to false.
*/
private void resetStatusValues() {
dupToken = false;
oldToken = false;
unseqToken = false;
gapToken = false;
minorStatus = 0;
minorString = null;
}
}

View file

@ -0,0 +1,216 @@
/*
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.ietf.jgss;
import java.io.InputStream;
import java.io.IOException;
import sun.security.util.DerValue;
import sun.security.util.DerOutputStream;
import sun.security.util.ObjectIdentifier;
/**
* This class represents Universal Object Identifiers (Oids) and their
* associated operations.<p>
*
* Oids are hierarchically globally-interpretable identifiers used
* within the GSS-API framework to identify mechanisms and name formats.<p>
*
* The structure and encoding of Oids is defined in ISOIEC-8824 and
* ISOIEC-8825. For example the Oid representation of Kerberos V5
* mechanism is "1.2.840.113554.1.2.2"<p>
*
* The GSSName name class contains public static Oid objects
* representing the standard name types defined in GSS-API.
*
* @author Mayank Upadhyay
* @since 1.4
*/
public class Oid {
private ObjectIdentifier oid;
private byte[] derEncoding;
/**
* Constructs an Oid object from a string representation of its
* integer components.
*
* @param strOid the dot separated string representation of the oid.
* For instance, "1.2.840.113554.1.2.2".
* @exception GSSException may be thrown when the string is incorrectly
* formatted
*/
public Oid(String strOid) throws GSSException {
try {
oid = new ObjectIdentifier(strOid);
derEncoding = null;
} catch (Exception e) {
throw new GSSException(GSSException.FAILURE,
"Improperly formatted Object Identifier String - "
+ strOid);
}
}
/**
* Creates an Oid object from its ASN.1 DER encoding. This refers to
* the full encoding including tag and length. The structure and
* encoding of Oids is defined in ISOIEC-8824 and ISOIEC-8825. This
* method is identical in functionality to its byte array counterpart.
*
* @param derOid stream containing the DER encoded oid
* @exception GSSException may be thrown when the DER encoding does not
* follow the prescribed format.
*/
public Oid(InputStream derOid) throws GSSException {
try {
DerValue derVal = new DerValue(derOid);
derEncoding = derVal.toByteArray();
oid = derVal.getOID();
} catch (IOException e) {
throw new GSSException(GSSException.FAILURE,
"Improperly formatted ASN.1 DER encoding for Oid");
}
}
/**
* Creates an Oid object from its ASN.1 DER encoding. This refers to
* the full encoding including tag and length. The structure and
* encoding of Oids is defined in ISOIEC-8824 and ISOIEC-8825. This
* method is identical in functionality to its InputStream conterpart.
*
* @param data byte array containing the DER encoded oid
* @exception GSSException may be thrown when the DER encoding does not
* follow the prescribed format.
*/
public Oid(byte [] data) throws GSSException {
try {
DerValue derVal = new DerValue(data);
derEncoding = derVal.toByteArray();
oid = derVal.getOID();
} catch (IOException e) {
throw new GSSException(GSSException.FAILURE,
"Improperly formatted ASN.1 DER encoding for Oid");
}
}
/**
* Only for calling by initializators used with declarations.
*
* @param strOid
*/
static Oid getInstance(String strOid) {
Oid retVal = null;
try {
retVal = new Oid(strOid);
} catch (GSSException e) {
// squelch it!
}
return retVal;
}
/**
* Returns a string representation of the oid's integer components
* in dot separated notation.
*
* @return string representation in the following format: "1.2.3.4.5"
*/
public String toString() {
return oid.toString();
}
/**
* Tests if two Oid objects represent the same Object identifier
* value.
*
* @return <code>true</code> if the two Oid objects represent the same
* value, <code>false</code> otherwise.
* @param other the Oid object that has to be compared to this one
*/
public boolean equals(Object other) {
//check if both reference the same object
if (this == other)
return (true);
if (other instanceof Oid)
return this.oid.equals(((Oid) other).oid);
else if (other instanceof ObjectIdentifier)
return this.oid.equals(other);
else
return false;
}
/**
* Returns the full ASN.1 DER encoding for this oid object, which
* includes the tag and length.
*
* @return byte array containing the DER encoding of this oid object.
* @exception GSSException may be thrown when the oid can't be encoded
*/
public byte[] getDER() throws GSSException {
if (derEncoding == null) {
DerOutputStream dout = new DerOutputStream();
try {
dout.putOID(oid);
} catch (IOException e) {
throw new GSSException(GSSException.FAILURE, e.getMessage());
}
derEncoding = dout.toByteArray();
}
return derEncoding.clone();
}
/**
* A utility method to test if this Oid value is contained within the
* supplied Oid array.
*
* @param oids the array of Oid's to search
* @return true if the array contains this Oid value, false otherwise
*/
public boolean containedIn(Oid[] oids) {
for (int i = 0; i < oids.length; i++) {
if (oids[i].equals(this))
return (true);
}
return (false);
}
/**
* Returns a hashcode value for this Oid.
*
* @return a hashCode value
*/
public int hashCode() {
return oid.hashCode();
}
}

View file

@ -0,0 +1,126 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<!--
Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 only, as
published by the Free Software Foundation. Oracle designates this
particular file as subject to the "Classpath" exception as provided
by Oracle in the LICENSE file that accompanied this code.
This code is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
version 2 for more details (a copy is included in the LICENSE file that
accompanied this code).
You should have received a copy of the GNU General Public License version
2 along with this work; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
or visit www.oracle.com if you need additional information or have any
questions.
-->
</head>
<body bgcolor="white">
This package presents a framework that allows application developers to
make use of security services like authentication, data integrity and
data confidentiality from a variety of underlying security mechanisms
like Kerberos, using a unified API. The security mechanisms that an
application can
chose to use are identified with unique object identifiers. One example
of such a mechanism is the Kerberos v5 GSS-API mechanism (object
identifier 1.2.840.113554.1.2.2). This mechanism is available through
the default instance of the GSSManager class.<p>
The GSS-API is defined in a language independent way in
<a href=http://www.ietf.org/rfc/rfc2743.txt>RFC 2743</a>. The Java
language bindings are defined in
<a href=http://www.ietf.org/rfc/rfc2853.txt>RFC 2853</a><p>
An application starts out by instantiating a <code>GSSManager</code>
which then serves as a factory for a security context. An application
can use specific principal names and credentials that are also created
using the GSSManager; or it can instantiate a
context with system defaults. It then goes through a context
establishment loop. Once a context is established with the
peer, authentication is complete. Data protection such as integrity
and confidentiality can then be obtained from this context.<p>
The GSS-API does not perform any communication with the peer. It merely
produces tokens that the application must somehow transport to the
other end.
<h3 id="useSubjectCredsOnly">Credential Acquisition</h3>
The GSS-API itself does not dictate how an underlying mechanism
obtains the credentials that are needed for authentication. It is
assumed that prior to calling the GSS-API, these credentials are
obtained and stored in a location that the mechanism provider is
aware of. However, the default model in the Java platform will be
that mechanism providers must obtain credentials only from the private
or public credential sets associated with the
{@link javax.security.auth.Subject Subject} in the
current access control context. The Kerberos v5
mechanism will search for the required INITIATE and ACCEPT credentials
({@link javax.security.auth.kerberos.KerberosTicket KerberosTicket} and
{@link javax.security.auth.kerberos.KerberosKey KerberosKey}) in
the private credential set where as some other mechanism might look
in the public set or in both. If the desired credential is not
present in the appropriate sets of the current Subject, the GSS-API
call must fail.<p>
This model has the advantage that credential management
is simple and predictable from the applications point of view. An
application, given the right permissions, can purge the credentials in
the Subject or renew them using standard Java API's. If it purged
the credentials, it would be sure that the JGSS mechanism would fail,
or if it renewed a time based credential it would be sure that a JGSS
mechanism would succeed.<p>
This model does require that a {@link
javax.security.auth.login JAAS login} be performed in order to
authenticate and populate a Subject that the JGSS mechanism can later
utilize. However, applications have the ability to relax this
restriction by means of a system property:
<code>javax.security.auth.useSubjectCredsOnly</code>. By default
this system property will be assumed to be <code>true</code> (even when
it is unset) indicating that providers must only use the credentials
that are present in the current Subject. However, if this property is
explicitly set to false by the application, then it indicates that
the provider is free to use any credentials cache of its choice. Such
a credential cache might be a disk cache, an in-memory cache, or even
just the current Subject itself.
<h2>Related Documentation</h2>
<p>
For an online tutorial on using Java GSS-API, please see
{@extLink security_guide_jgss_tutorial
Introduction to JAAS and Java GSS-API}.
</p>
<!--
<h2>Package Specification</h2>
##### FILL IN ANY SPECS NEEDED BY JAVA COMPATIBILITY KIT #####
<ul>
<li><a href="">##### REFER TO ANY FRAMEMAKER SPECIFICATION HERE #####</a>
</ul>
<h2>Related Documentation</h2>
For overviews, tutorials, examples, guides, and tool documentation, please see:
<ul>
<li><a href="">##### REFER TO NON-SPEC DOCUMENTATION HERE #####</a>
</ul>
-->
@since 1.4
</body>
</html>

View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.http.spnego;
import java.io.IOException;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.util.Arrays;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import sun.net.www.protocol.http.HttpCallerInfo;
/**
* @since 1.6
* Special callback handler used in JGSS for the HttpCaller.
*/
public class NegotiateCallbackHandler implements CallbackHandler {
private String username;
private char[] password;
/**
* Authenticator asks for username and password in a single prompt,
* but CallbackHandler checks one by one. So, no matter which callback
* gets handled first, make sure Authenticator is only called once.
*/
private boolean answered;
private final HttpCallerInfo hci;
public NegotiateCallbackHandler(HttpCallerInfo hci) {
this.hci = hci;
}
private void getAnswer() {
if (!answered) {
answered = true;
PasswordAuthentication passAuth =
Authenticator.requestPasswordAuthentication(
hci.authenticator,
hci.host, hci.addr, hci.port, hci.protocol,
hci.prompt, hci.scheme, hci.url, hci.authType);
/**
* To be compatible with existing callback handler implementations,
* when the underlying Authenticator is canceled, username and
* password are assigned null. No exception is thrown.
*/
if (passAuth != null) {
username = passAuth.getUserName();
password = passAuth.getPassword();
}
}
}
public void handle(Callback[] callbacks) throws
UnsupportedCallbackException, IOException {
for (int i=0; i<callbacks.length; i++) {
Callback callBack = callbacks[i];
if (callBack instanceof NameCallback) {
getAnswer();
((NameCallback)callBack).setName(username);
} else if (callBack instanceof PasswordCallback) {
getAnswer();
((PasswordCallback)callBack).setPassword(password);
if (password != null) Arrays.fill(password, ' ');
} else {
throw new UnsupportedCallbackException(callBack,
"Call back not supported");
}
}
}
}

View file

@ -0,0 +1,159 @@
/*
* Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.www.protocol.http.spnego;
import java.io.IOException;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
import sun.net.www.protocol.http.HttpCallerInfo;
import sun.net.www.protocol.http.Negotiator;
import sun.security.jgss.GSSManagerImpl;
import sun.security.jgss.GSSContextImpl;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.HttpCaller;
/**
* This class encapsulates all JAAS and JGSS API calls in a separate class
* outside NegotiateAuthentication.java so that J2SE build can go smoothly
* without the presence of it.
*
* @author weijun.wang@sun.com
* @since 1.6
*/
public class NegotiatorImpl extends Negotiator {
private static final boolean DEBUG =
java.security.AccessController.doPrivileged(
new sun.security.action.GetBooleanAction("sun.security.krb5.debug"));
private GSSContext context;
private byte[] oneToken;
/**
* Initialize the object, which includes:<ul>
* <li>Find out what GSS mechanism to use from the system property
* <code>http.negotiate.mechanism.oid</code>, defaults SPNEGO
* <li>Creating the GSSName for the target host, "HTTP/"+hostname
* <li>Creating GSSContext
* <li>A first call to initSecContext</ul>
*/
private void init(HttpCallerInfo hci) throws GSSException {
final Oid oid;
if (hci.scheme.equalsIgnoreCase("Kerberos")) {
// we can only use Kerberos mech when the scheme is kerberos
oid = GSSUtil.GSS_KRB5_MECH_OID;
} else {
String pref = java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<String>() {
public String run() {
return System.getProperty(
"http.auth.preference",
"spnego");
}
});
if (pref.equalsIgnoreCase("kerberos")) {
oid = GSSUtil.GSS_KRB5_MECH_OID;
} else {
// currently there is no 3rd mech we can use
oid = GSSUtil.GSS_SPNEGO_MECH_OID;
}
}
GSSManagerImpl manager = new GSSManagerImpl(
new HttpCaller(hci));
// RFC 4559 4.1 uses uppercase service name "HTTP".
// RFC 4120 6.2.1 demands the host be lowercase
String peerName = "HTTP@" + hci.host.toLowerCase();
GSSName serverName = manager.createName(peerName,
GSSName.NT_HOSTBASED_SERVICE);
context = manager.createContext(serverName,
oid,
null,
GSSContext.DEFAULT_LIFETIME);
// Always respect delegation policy in HTTP/SPNEGO.
if (context instanceof GSSContextImpl) {
((GSSContextImpl)context).requestDelegPolicy(true);
}
oneToken = context.initSecContext(new byte[0], 0, 0);
}
/**
* Constructor
* @throws java.io.IOException If negotiator cannot be constructed
*/
public NegotiatorImpl(HttpCallerInfo hci) throws IOException {
try {
init(hci);
} catch (GSSException e) {
if (DEBUG) {
System.out.println("Negotiate support not initiated, will " +
"fallback to other scheme if allowed. Reason:");
e.printStackTrace();
}
IOException ioe = new IOException("Negotiate support not initiated");
ioe.initCause(e);
throw ioe;
}
}
/**
* Return the first token of GSS, in SPNEGO, it's called NegTokenInit
* @return the first token
*/
@Override
public byte[] firstToken() {
return oneToken;
}
/**
* Return the rest tokens of GSS, in SPNEGO, it's called NegTokenTarg
* @param token the token received from server
* @return the next token
* @throws java.io.IOException if the token cannot be created successfully
*/
@Override
public byte[] nextToken(byte[] token) throws IOException {
try {
return context.initSecContext(token, 0, token.length);
} catch (GSSException e) {
if (DEBUG) {
System.out.println("Negotiate support cannot continue. Reason:");
e.printStackTrace();
}
IOException ioe = new IOException("Negotiate support cannot continue");
ioe.initCause(e);
throw ioe;
}
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
/**
* Denotes what client is calling the JGSS-API. The object can be sent deep
* into the mechanism level so that special actions can be performed for
* different callers.
*/
public class GSSCaller {
public static final GSSCaller CALLER_UNKNOWN = new GSSCaller("UNKNOWN");
public static final GSSCaller CALLER_INITIATE = new GSSCaller("INITIATE");
public static final GSSCaller CALLER_ACCEPT = new GSSCaller("ACCEPT");
public static final GSSCaller CALLER_SSL_CLIENT = new GSSCaller("SSL_CLIENT");
public static final GSSCaller CALLER_SSL_SERVER = new GSSCaller("SSL_SERVER");
private String name;
GSSCaller(String s) {
name = s;
}
@Override
public String toString() {
return "GSSCaller{" + name + '}';
}
}

View file

@ -0,0 +1,671 @@
/*
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import sun.security.util.ObjectIdentifier;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/**
* This class represents the JGSS security context and its associated
* operations. JGSS security contexts are established between
* peers using locally established credentials. Multiple contexts
* may exist simultaneously between a pair of peers, using the same
* or different set of credentials. The JGSS is independent of
* the underlying transport protocols and depends on its callers to
* transport the tokens between peers.
* <p>
* The context object can be thought of as having 3 implicit states:
* before it is established, during its context establishment, and
* after a fully established context exists.
* <p>
* Before the context establishment phase is initiated, the context
* initiator may request specific characteristics desired of the
* established context. These can be set using the set methods. After the
* context is established, the caller can check the actual characteristic
* and services offered by the context using the query methods.
* <p>
* The context establishment phase begins with the first call to the
* initSecContext method by the context initiator. During this phase the
* initSecContext and acceptSecContext methods will produce GSS-API
* authentication tokens which the calling application needs to send to its
* peer. The initSecContext and acceptSecContext methods may
* return a CONTINUE_NEEDED code which indicates that a token is needed
* from its peer in order to continue the context establishment phase. A
* return code of COMPLETE signals that the local end of the context is
* established. This may still require that a token be sent to the peer,
* depending if one is produced by GSS-API. The isEstablished method can
* also be used to determine if the local end of the context has been
* fully established. During the context establishment phase, the
* isProtReady method may be called to determine if the context can be
* used for the per-message operations. This allows implementation to
* use per-message operations on contexts which aren't fully established.
* <p>
* After the context has been established or the isProtReady method
* returns "true", the query routines can be invoked to determine the actual
* characteristics and services of the established context. The
* application can also start using the per-message methods of wrap and
* getMIC to obtain cryptographic operations on application supplied data.
* <p>
* When the context is no longer needed, the application should call
* dispose to release any system resources the context may be using.
* <DL><DT><B>RFC 2078</b>
* <DD>This class corresponds to the context level calls together with
* the per message calls of RFC 2078. The gss_init_sec_context and
* gss_accept_sec_context calls have been made simpler by only taking
* required parameters. The context can have its properties set before
* the first call to initSecContext. The supplementary status codes for the
* per-message operations are returned in an instance of the MessageProp
* class, which is used as an argument in these calls.</dl>
*/
public class GSSContextImpl implements GSSContext {
private GSSManagerImpl gssManager;
private boolean initiator;
// private flags for the context state
private static final int PRE_INIT = 1;
private static final int IN_PROGRESS = 2;
private static final int READY = 3;
private static final int DELETED = 4;
// instance variables
private int currentState = PRE_INIT;
private GSSContextSpi mechCtxt = null;
private Oid mechOid = null;
private ObjectIdentifier objId = null;
private GSSCredentialImpl myCred = null;
private GSSNameImpl srcName = null;
private GSSNameImpl targName = null;
private int reqLifetime = INDEFINITE_LIFETIME;
private ChannelBinding channelBindings = null;
private boolean reqConfState = true;
private boolean reqIntegState = true;
private boolean reqMutualAuthState = true;
private boolean reqReplayDetState = true;
private boolean reqSequenceDetState = true;
private boolean reqCredDelegState = false;
private boolean reqAnonState = false;
private boolean reqDelegPolicyState = false;
public GSSContextImpl() {
// Useless
}
// Used by new ExtendedGSSContext.ExtendedGSSContextImpl(ctxt)
protected GSSContextImpl(GSSContextImpl src) {
for (Field f: GSSContextImpl.class.getDeclaredFields()) {
if (!Modifier.isStatic(f.getModifiers())) {
try {
f.set(this, f.get(src));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
/**
* Creates a GSSContextImp on the context initiator's side.
*/
public GSSContextImpl(GSSManagerImpl gssManager, GSSName peer, Oid mech,
GSSCredential myCred, int lifetime)
throws GSSException {
if ((peer == null) || !(peer instanceof GSSNameImpl)) {
throw new GSSException(GSSException.BAD_NAME);
}
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
this.gssManager = gssManager;
this.myCred = (GSSCredentialImpl) myCred; // XXX Check first
reqLifetime = lifetime;
targName = (GSSNameImpl)peer;
this.mechOid = mech;
initiator = true;
}
/**
* Creates a GSSContextImpl on the context acceptor's side.
*/
public GSSContextImpl(GSSManagerImpl gssManager, GSSCredential myCred)
throws GSSException {
this.gssManager = gssManager;
this.myCred = (GSSCredentialImpl) myCred; // XXX Check first
initiator = false;
}
/**
* Creates a GSSContextImpl out of a previously exported
* GSSContext.
*
* @see #isTransferable
*/
public GSSContextImpl(GSSManagerImpl gssManager, byte[] interProcessToken)
throws GSSException {
this.gssManager = gssManager;
mechCtxt = gssManager.getMechanismContext(interProcessToken);
initiator = mechCtxt.isInitiator();
this.mechOid = mechCtxt.getMech();
}
public byte[] initSecContext(byte inputBuf[], int offset, int len)
throws GSSException {
/*
* Size of ByteArrayOutputStream will double each time that extra
* bytes are to be written. Usually, without delegation, a GSS
* initial token containing the Kerberos AP-REQ is between 400 and
* 600 bytes.
*/
ByteArrayOutputStream bos = new ByteArrayOutputStream(600);
ByteArrayInputStream bin =
new ByteArrayInputStream(inputBuf, offset, len);
int size = initSecContext(bin, bos);
return (size == 0? null : bos.toByteArray());
}
public int initSecContext(InputStream inStream,
OutputStream outStream) throws GSSException {
if (mechCtxt != null && currentState != IN_PROGRESS) {
throw new GSSExceptionImpl(GSSException.FAILURE,
"Illegal call to initSecContext");
}
GSSHeader gssHeader = null;
int inTokenLen = -1;
GSSCredentialSpi credElement = null;
boolean firstToken = false;
try {
if (mechCtxt == null) {
if (myCred != null) {
try {
credElement = myCred.getElement(mechOid, true);
} catch (GSSException ge) {
if (GSSUtil.isSpNegoMech(mechOid) &&
ge.getMajor() == GSSException.NO_CRED) {
credElement = myCred.getElement
(myCred.getMechs()[0], true);
} else {
throw ge;
}
}
}
GSSNameSpi nameElement = targName.getElement(mechOid);
mechCtxt = gssManager.getMechanismContext(nameElement,
credElement,
reqLifetime,
mechOid);
mechCtxt.requestConf(reqConfState);
mechCtxt.requestInteg(reqIntegState);
mechCtxt.requestCredDeleg(reqCredDelegState);
mechCtxt.requestMutualAuth(reqMutualAuthState);
mechCtxt.requestReplayDet(reqReplayDetState);
mechCtxt.requestSequenceDet(reqSequenceDetState);
mechCtxt.requestAnonymity(reqAnonState);
mechCtxt.setChannelBinding(channelBindings);
mechCtxt.requestDelegPolicy(reqDelegPolicyState);
objId = new ObjectIdentifier(mechOid.toString());
currentState = IN_PROGRESS;
firstToken = true;
} else {
if (mechCtxt.getProvider().getName().equals("SunNativeGSS") ||
GSSUtil.isSpNegoMech(mechOid)) {
// do not parse GSS header for native provider or SPNEGO
// mech
} else {
// parse GSS header
gssHeader = new GSSHeader(inStream);
if (!gssHeader.getOid().equals(objId))
throw new GSSExceptionImpl
(GSSException.DEFECTIVE_TOKEN,
"Mechanism not equal to " +
mechOid.toString() +
" in initSecContext token");
inTokenLen = gssHeader.getMechTokenLength();
}
}
byte[] obuf = mechCtxt.initSecContext(inStream, inTokenLen);
int retVal = 0;
if (obuf != null) {
retVal = obuf.length;
if (mechCtxt.getProvider().getName().equals("SunNativeGSS") ||
(!firstToken && GSSUtil.isSpNegoMech(mechOid))) {
// do not add GSS header for native provider or SPNEGO
// except for the first SPNEGO token
} else {
// add GSS header
gssHeader = new GSSHeader(objId, obuf.length);
retVal += gssHeader.encode(outStream);
}
outStream.write(obuf);
}
if (mechCtxt.isEstablished())
currentState = READY;
return retVal;
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.DEFECTIVE_TOKEN,
e.getMessage());
}
}
public byte[] acceptSecContext(byte inTok[], int offset, int len)
throws GSSException {
/*
* Usually initial GSS token containing a Kerberos AP-REP is less
* than 100 bytes.
*/
ByteArrayOutputStream bos = new ByteArrayOutputStream(100);
acceptSecContext(new ByteArrayInputStream(inTok, offset, len),
bos);
byte[] out = bos.toByteArray();
return (out.length == 0) ? null : out;
}
public void acceptSecContext(InputStream inStream,
OutputStream outStream) throws GSSException {
if (mechCtxt != null && currentState != IN_PROGRESS) {
throw new GSSExceptionImpl(GSSException.FAILURE,
"Illegal call to acceptSecContext");
}
GSSHeader gssHeader = null;
int inTokenLen = -1;
GSSCredentialSpi credElement = null;
try {
if (mechCtxt == null) {
// mechOid will be null for an acceptor's context
gssHeader = new GSSHeader(inStream);
inTokenLen = gssHeader.getMechTokenLength();
/*
* Convert ObjectIdentifier to Oid
*/
objId = gssHeader.getOid();
mechOid = new Oid(objId.toString());
// System.out.println("Entered GSSContextImpl.acceptSecContext"
// + " with mechanism = " + mechOid);
if (myCred != null) {
credElement = myCred.getElement(mechOid, false);
}
mechCtxt = gssManager.getMechanismContext(credElement,
mechOid);
mechCtxt.setChannelBinding(channelBindings);
currentState = IN_PROGRESS;
} else {
if (mechCtxt.getProvider().getName().equals("SunNativeGSS") ||
(GSSUtil.isSpNegoMech(mechOid))) {
// do not parse GSS header for native provider and SPNEGO
} else {
// parse GSS Header
gssHeader = new GSSHeader(inStream);
if (!gssHeader.getOid().equals(objId))
throw new GSSExceptionImpl
(GSSException.DEFECTIVE_TOKEN,
"Mechanism not equal to " +
mechOid.toString() +
" in acceptSecContext token");
inTokenLen = gssHeader.getMechTokenLength();
}
}
byte[] obuf = mechCtxt.acceptSecContext(inStream, inTokenLen);
if (obuf != null) {
int retVal = obuf.length;
if (mechCtxt.getProvider().getName().equals("SunNativeGSS") ||
(GSSUtil.isSpNegoMech(mechOid))) {
// do not add GSS header for native provider and SPNEGO
} else {
// add GSS header
gssHeader = new GSSHeader(objId, obuf.length);
retVal += gssHeader.encode(outStream);
}
outStream.write(obuf);
}
if (mechCtxt.isEstablished()) {
currentState = READY;
}
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.DEFECTIVE_TOKEN,
e.getMessage());
}
}
public boolean isEstablished() {
if (mechCtxt == null)
return false;
else
return (currentState == READY);
}
public int getWrapSizeLimit(int qop, boolean confReq,
int maxTokenSize) throws GSSException {
if (mechCtxt != null)
return mechCtxt.getWrapSizeLimit(qop, confReq, maxTokenSize);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public byte[] wrap(byte inBuf[], int offset, int len,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
return mechCtxt.wrap(inBuf, offset, len, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public void wrap(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
mechCtxt.wrap(inStream, outStream, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public byte [] unwrap(byte[] inBuf, int offset, int len,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
return mechCtxt.unwrap(inBuf, offset, len, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public void unwrap(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
mechCtxt.unwrap(inStream, outStream, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public byte[] getMIC(byte []inMsg, int offset, int len,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
return mechCtxt.getMIC(inMsg, offset, len, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public void getMIC(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
mechCtxt.getMIC(inStream, outStream, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public void verifyMIC(byte[] inTok, int tokOffset, int tokLen,
byte[] inMsg, int msgOffset, int msgLen,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
mechCtxt.verifyMIC(inTok, tokOffset, tokLen,
inMsg, msgOffset, msgLen, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public void verifyMIC(InputStream tokStream, InputStream msgStream,
MessageProp msgProp) throws GSSException {
if (mechCtxt != null)
mechCtxt.verifyMIC(tokStream, msgStream, msgProp);
else
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
}
public byte[] export() throws GSSException {
// Defaults to null to match old behavior
byte[] result = null;
// Only allow context export from native provider since JGSS
// still has not defined its own interprocess token format
if (mechCtxt.isTransferable() &&
mechCtxt.getProvider().getName().equals("SunNativeGSS")) {
result = mechCtxt.export();
}
return result;
}
public void requestMutualAuth(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqMutualAuthState = state;
}
public void requestReplayDet(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqReplayDetState = state;
}
public void requestSequenceDet(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqSequenceDetState = state;
}
public void requestCredDeleg(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqCredDelegState = state;
}
public void requestAnonymity(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqAnonState = state;
}
public void requestConf(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqConfState = state;
}
public void requestInteg(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqIntegState = state;
}
public void requestLifetime(int lifetime) throws GSSException {
if (mechCtxt == null && initiator)
reqLifetime = lifetime;
}
public void setChannelBinding(ChannelBinding channelBindings)
throws GSSException {
if (mechCtxt == null)
this.channelBindings = channelBindings;
}
public boolean getCredDelegState() {
if (mechCtxt != null)
return mechCtxt.getCredDelegState();
else
return reqCredDelegState;
}
public boolean getMutualAuthState() {
if (mechCtxt != null)
return mechCtxt.getMutualAuthState();
else
return reqMutualAuthState;
}
public boolean getReplayDetState() {
if (mechCtxt != null)
return mechCtxt.getReplayDetState();
else
return reqReplayDetState;
}
public boolean getSequenceDetState() {
if (mechCtxt != null)
return mechCtxt.getSequenceDetState();
else
return reqSequenceDetState;
}
public boolean getAnonymityState() {
if (mechCtxt != null)
return mechCtxt.getAnonymityState();
else
return reqAnonState;
}
public boolean isTransferable() throws GSSException {
if (mechCtxt != null)
return mechCtxt.isTransferable();
else
return false;
}
public boolean isProtReady() {
if (mechCtxt != null)
return mechCtxt.isProtReady();
else
return false;
}
public boolean getConfState() {
if (mechCtxt != null)
return mechCtxt.getConfState();
else
return reqConfState;
}
public boolean getIntegState() {
if (mechCtxt != null)
return mechCtxt.getIntegState();
else
return reqIntegState;
}
public int getLifetime() {
if (mechCtxt != null)
return mechCtxt.getLifetime();
else
return reqLifetime;
}
public GSSName getSrcName() throws GSSException {
if (srcName == null) {
srcName = GSSNameImpl.wrapElement
(gssManager, mechCtxt.getSrcName());
}
return srcName;
}
public GSSName getTargName() throws GSSException {
if (targName == null) {
targName = GSSNameImpl.wrapElement
(gssManager, mechCtxt.getTargName());
}
return targName;
}
public Oid getMech() throws GSSException {
if (mechCtxt != null) {
return mechCtxt.getMech();
}
return mechOid;
}
public GSSCredential getDelegCred() throws GSSException {
if (mechCtxt == null)
throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
"No mechanism context yet!");
GSSCredentialSpi delCredElement = mechCtxt.getDelegCred();
return (delCredElement == null ?
null : GSSManagerImpl.wrap(new GSSCredentialImpl(gssManager, delCredElement)));
}
public boolean isInitiator() throws GSSException {
return initiator;
}
public void dispose() throws GSSException {
currentState = DELETED;
if (mechCtxt != null) {
mechCtxt.dispose();
mechCtxt = null;
}
myCred = null;
srcName = null;
targName = null;
}
// ExtendedGSSContext methods:
public Object inquireSecContext(String type) throws GSSException {
if (mechCtxt == null) {
throw new GSSException(GSSException.NO_CONTEXT);
}
return mechCtxt.inquireSecContext(type);
}
public void requestDelegPolicy(boolean state) throws GSSException {
if (mechCtxt == null && initiator)
reqDelegPolicyState = state;
}
public boolean getDelegPolicyState() {
if (mechCtxt != null)
return mechCtxt.getDelegPolicyState();
else
return reqDelegPolicyState;
}
}

View file

@ -0,0 +1,702 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import java.util.*;
import sun.security.jgss.spnego.SpNegoCredElement;
public class GSSCredentialImpl implements GSSCredential {
private GSSManagerImpl gssManager = null;
private boolean destroyed = false;
/*
* We store all elements in a hashtable, using <oid, usage> as the
* key. This makes it easy to locate the specific kind of credential we
* need. The implementation needs to be optimized for the case where
* there is just one element (tempCred).
*/
private Hashtable<SearchKey, GSSCredentialSpi> hashtable = null;
// XXX Optimization for single mech usage
private GSSCredentialSpi tempCred = null;
public GSSCredentialImpl() {
// Useless
}
// Used by new ExtendedGSSCredential.ExtendedGSSCredentialImpl(cred)
protected GSSCredentialImpl(GSSCredentialImpl src) {
this.gssManager = src.gssManager;
this.destroyed = src.destroyed;
this.hashtable = src.hashtable;
this.tempCred = src.tempCred;
}
GSSCredentialImpl(GSSManagerImpl gssManager, int usage)
throws GSSException {
this(gssManager, null, GSSCredential.DEFAULT_LIFETIME,
(Oid[]) null, usage);
}
GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name,
int lifetime, Oid mech, int usage)
throws GSSException {
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
init(gssManager);
add(name, lifetime, lifetime, mech, usage);
}
GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name,
int lifetime, Oid[] mechs, int usage)
throws GSSException {
init(gssManager);
boolean defaultList = false;
if (mechs == null) {
mechs = gssManager.getMechs();
defaultList = true;
}
for (int i = 0; i < mechs.length; i++) {
try {
add(name, lifetime, lifetime, mechs[i], usage);
} catch (GSSException e) {
if (defaultList) {
// Try the next mechanism
GSSUtil.debug("Ignore " + e + " while acquring cred for "
+ mechs[i]);
//e.printStackTrace();
} else throw e; // else try the next mechanism
}
}
if ((hashtable.size() == 0) || (usage != getUsage()))
throw new GSSException(GSSException.NO_CRED);
}
// Wrap a mech cred into a GSS cred
public GSSCredentialImpl(GSSManagerImpl gssManager,
GSSCredentialSpi mechElement) throws GSSException {
init(gssManager);
int usage = GSSCredential.ACCEPT_ONLY;
if (mechElement.isInitiatorCredential()) {
if (mechElement.isAcceptorCredential()) {
usage = GSSCredential.INITIATE_AND_ACCEPT;
} else {
usage = GSSCredential.INITIATE_ONLY;
}
}
SearchKey key = new SearchKey(mechElement.getMechanism(),
usage);
tempCred = mechElement;
hashtable.put(key, tempCred);
// More mechs that can use this cred, say, SPNEGO
if (!GSSUtil.isSpNegoMech(mechElement.getMechanism())) {
key = new SearchKey(GSSUtil.GSS_SPNEGO_MECH_OID, usage);
hashtable.put(key, new SpNegoCredElement(mechElement));
}
}
void init(GSSManagerImpl gssManager) {
this.gssManager = gssManager;
hashtable = new Hashtable<SearchKey, GSSCredentialSpi>(
gssManager.getMechs().length);
}
public void dispose() throws GSSException {
if (!destroyed) {
GSSCredentialSpi element;
Enumeration<GSSCredentialSpi> values = hashtable.elements();
while (values.hasMoreElements()) {
element = values.nextElement();
element.dispose();
}
destroyed = true;
}
}
public GSSCredential impersonate(GSSName name) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
Oid mech = tempCred.getMechanism();
GSSNameSpi nameElement = (name == null ? null :
((GSSNameImpl)name).getElement(mech));
GSSCredentialSpi cred = tempCred.impersonate(nameElement);
return (cred == null ?
null : GSSManagerImpl.wrap(new GSSCredentialImpl(gssManager, cred)));
}
public GSSName getName() throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
return GSSNameImpl.wrapElement(gssManager, tempCred.getName());
}
public GSSName getName(Oid mech) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
SearchKey key = null;
GSSCredentialSpi element = null;
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
key = new SearchKey(mech, GSSCredential.INITIATE_ONLY);
element = hashtable.get(key);
if (element == null) {
key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY);
element = hashtable.get(key);
}
if (element == null) {
key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
element = hashtable.get(key);
}
if (element == null) {
throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
}
return GSSNameImpl.wrapElement(gssManager, element.getName());
}
/**
* Returns the remaining lifetime of this credential. The remaining
* lifetime is defined as the minimum lifetime, either for initiate or
* for accept, across all elements contained in it. Not terribly
* useful, but required by GSS-API.
*/
public int getRemainingLifetime() throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
SearchKey tempKey;
GSSCredentialSpi tempCred;
int tempLife = 0, tempInitLife = 0, tempAcceptLife = 0;
int min = INDEFINITE_LIFETIME;
for (Enumeration<SearchKey> e = hashtable.keys();
e.hasMoreElements(); ) {
tempKey = e.nextElement();
tempCred = hashtable.get(tempKey);
if (tempKey.getUsage() == INITIATE_ONLY)
tempLife = tempCred.getInitLifetime();
else if (tempKey.getUsage() == ACCEPT_ONLY)
tempLife = tempCred.getAcceptLifetime();
else {
tempInitLife = tempCred.getInitLifetime();
tempAcceptLife = tempCred.getAcceptLifetime();
tempLife = (tempInitLife < tempAcceptLife ?
tempInitLife:
tempAcceptLife);
}
if (min > tempLife)
min = tempLife;
}
return min;
}
public int getRemainingInitLifetime(Oid mech) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
GSSCredentialSpi element = null;
SearchKey key = null;
boolean found = false;
int max = 0;
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
key = new SearchKey(mech, GSSCredential.INITIATE_ONLY);
element = hashtable.get(key);
if (element != null) {
found = true;
if (max < element.getInitLifetime())
max = element.getInitLifetime();
}
key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
element = hashtable.get(key);
if (element != null) {
found = true;
if (max < element.getInitLifetime())
max = element.getInitLifetime();
}
if (!found) {
throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
}
return max;
}
public int getRemainingAcceptLifetime(Oid mech) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
GSSCredentialSpi element = null;
SearchKey key = null;
boolean found = false;
int max = 0;
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY);
element = hashtable.get(key);
if (element != null) {
found = true;
if (max < element.getAcceptLifetime())
max = element.getAcceptLifetime();
}
key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
element = hashtable.get(key);
if (element != null) {
found = true;
if (max < element.getAcceptLifetime())
max = element.getAcceptLifetime();
}
if (!found) {
throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
}
return max;
}
/**
* Returns the usage mode for this credential. Returns
* INITIATE_AND_ACCEPT if any one element contained in it supports
* INITIATE_AND_ACCEPT or if two different elements exist where one
* support INITIATE_ONLY and the other supports ACCEPT_ONLY.
*/
public int getUsage() throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
SearchKey tempKey;
boolean initiate = false;
boolean accept = false;
for (Enumeration<SearchKey> e = hashtable.keys();
e.hasMoreElements(); ) {
tempKey = e.nextElement();
if (tempKey.getUsage() == INITIATE_ONLY)
initiate = true;
else if (tempKey.getUsage() == ACCEPT_ONLY)
accept = true;
else
return INITIATE_AND_ACCEPT;
}
if (initiate) {
if (accept)
return INITIATE_AND_ACCEPT;
else
return INITIATE_ONLY;
} else
return ACCEPT_ONLY;
}
public int getUsage(Oid mech) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
GSSCredentialSpi element = null;
SearchKey key = null;
boolean initiate = false;
boolean accept = false;
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
key = new SearchKey(mech, GSSCredential.INITIATE_ONLY);
element = hashtable.get(key);
if (element != null) {
initiate = true;
}
key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY);
element = hashtable.get(key);
if (element != null) {
accept = true;
}
key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
element = hashtable.get(key);
if (element != null) {
initiate = true;
accept = true;
}
if (initiate && accept)
return GSSCredential.INITIATE_AND_ACCEPT;
else if (initiate)
return GSSCredential.INITIATE_ONLY;
else if (accept)
return GSSCredential.ACCEPT_ONLY;
else {
throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
}
}
public Oid[] getMechs() throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
Vector<Oid> result = new Vector<Oid>(hashtable.size());
for (Enumeration<SearchKey> e = hashtable.keys();
e.hasMoreElements(); ) {
SearchKey tempKey = e.nextElement();
result.addElement(tempKey.getMech());
}
return result.toArray(new Oid[0]);
}
public void add(GSSName name, int initLifetime, int acceptLifetime,
Oid mech, int usage) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
SearchKey key = new SearchKey(mech, usage);
if (hashtable.containsKey(key)) {
throw new GSSExceptionImpl(GSSException.DUPLICATE_ELEMENT,
"Duplicate element found: " +
getElementStr(mech, usage));
}
// XXX If not instance of GSSNameImpl then throw exception
// Application mixing GSS implementations
GSSNameSpi nameElement = (name == null ? null :
((GSSNameImpl)name).getElement(mech));
tempCred = gssManager.getCredentialElement(nameElement,
initLifetime,
acceptLifetime,
mech,
usage);
/*
* Not all mechanisms support the concept of one credential element
* that can be used for both initiating and accepting a context. In
* the event that an application requests usage INITIATE_AND_ACCEPT
* for a credential from such a mechanism, the GSS framework will
* need to obtain two different credential elements from the
* mechanism, one that will have usage INITIATE_ONLY and another
* that will have usage ACCEPT_ONLY. The mechanism will help the
* GSS-API realize this by returning a credential element with
* usage INITIATE_ONLY or ACCEPT_ONLY prompting it to make another
* call to getCredentialElement, this time with the other usage
* mode.
*/
if (tempCred != null) {
if (usage == GSSCredential.INITIATE_AND_ACCEPT &&
(!tempCred.isAcceptorCredential() ||
!tempCred.isInitiatorCredential())) {
int currentUsage;
int desiredUsage;
if (!tempCred.isInitiatorCredential()) {
currentUsage = GSSCredential.ACCEPT_ONLY;
desiredUsage = GSSCredential.INITIATE_ONLY;
} else {
currentUsage = GSSCredential.INITIATE_ONLY;
desiredUsage = GSSCredential.ACCEPT_ONLY;
}
key = new SearchKey(mech, currentUsage);
hashtable.put(key, tempCred);
tempCred = gssManager.getCredentialElement(nameElement,
initLifetime,
acceptLifetime,
mech,
desiredUsage);
key = new SearchKey(mech, desiredUsage);
hashtable.put(key, tempCred);
} else {
hashtable.put(key, tempCred);
}
}
}
public boolean equals(Object another) {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
if (this == another) {
return true;
}
if (!(another instanceof GSSCredentialImpl)) {
return false;
}
// NOTE: The specification does not define the criteria to compare
// credentials.
/*
* XXX
* The RFC says: "Tests if this GSSCredential refers to the same
* entity as the supplied object. The two credentials must be
* acquired over the same mechanisms and must refer to the same
* principal. Returns "true" if the two GSSCredentials refer to
* the same entity; "false" otherwise."
*
* Well, when do two credentials refer to the same principal? Do
* they need to have one GSSName in common for the different
* GSSName's that the credential elements return? Or do all
* GSSName's have to be in common when the names are exported with
* their respective mechanisms for the credential elements?
*/
return false;
}
/**
* Returns a hashcode value for this GSSCredential.
*
* @return a hashCode value
*/
public int hashCode() {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
// NOTE: The specification does not define the criteria to compare
// credentials.
/*
* XXX
* Decide on a criteria for equals first then do this.
*/
return 1;
}
/**
* Returns the specified mechanism's credential-element.
*
* @param mechOid the oid for mechanism to retrieve
* @param initiate boolean indicating if the function is
* to throw exception or return null when element is not
* found.
* @return mechanism credential object
* @exception GSSException of invalid mechanism
*/
public GSSCredentialSpi getElement(Oid mechOid, boolean initiate)
throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
SearchKey key;
GSSCredentialSpi element;
if (mechOid == null) {
/*
* First see if the default mechanism satisfies the
* desired usage.
*/
mechOid = ProviderList.DEFAULT_MECH_OID;
key = new SearchKey(mechOid,
initiate? INITIATE_ONLY : ACCEPT_ONLY);
element = hashtable.get(key);
if (element == null) {
key = new SearchKey(mechOid, INITIATE_AND_ACCEPT);
element = hashtable.get(key);
if (element == null) {
/*
* Now just return any element that satisfies the
* desired usage.
*/
Object[] elements = hashtable.entrySet().toArray();
for (int i = 0; i < elements.length; i++) {
element = (GSSCredentialSpi)
((Map.Entry)elements[i]).getValue();
if (element.isInitiatorCredential() == initiate)
break;
} // for loop
}
}
} else {
if (initiate)
key = new SearchKey(mechOid, INITIATE_ONLY);
else
key = new SearchKey(mechOid, ACCEPT_ONLY);
element = hashtable.get(key);
if (element == null) {
key = new SearchKey(mechOid, INITIATE_AND_ACCEPT);
element = hashtable.get(key);
}
}
if (element == null)
throw new GSSExceptionImpl(GSSException.NO_CRED,
"No credential found for: " +
getElementStr(mechOid,
initiate? INITIATE_ONLY : ACCEPT_ONLY));
return element;
}
Set<GSSCredentialSpi> getElements() {
HashSet<GSSCredentialSpi> retVal =
new HashSet<GSSCredentialSpi>(hashtable.size());
Enumeration<GSSCredentialSpi> values = hashtable.elements();
while (values.hasMoreElements()) {
GSSCredentialSpi o = values.nextElement();
retVal.add(o);
}
return retVal;
}
private static String getElementStr(Oid mechOid, int usage) {
String displayString = mechOid.toString();
if (usage == GSSCredential.INITIATE_ONLY) {
displayString =
displayString.concat(" usage: Initiate");
} else if (usage == GSSCredential.ACCEPT_ONLY) {
displayString =
displayString.concat(" usage: Accept");
} else {
displayString =
displayString.concat(" usage: Initiate and Accept");
}
return displayString;
}
public String toString() {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
GSSCredentialSpi element = null;
StringBuilder sb = new StringBuilder("[GSSCredential: ");
Object[] elements = hashtable.entrySet().toArray();
for (int i = 0; i < elements.length; i++) {
try {
sb.append('\n');
element = (GSSCredentialSpi)
((Map.Entry)elements[i]).getValue();
sb.append(element.getName());
sb.append(' ');
sb.append(element.getMechanism());
sb.append(element.isInitiatorCredential() ?
" Initiate" : "");
sb.append(element.isAcceptorCredential() ?
" Accept" : "");
sb.append(" [");
sb.append(element.getClass());
sb.append(']');
} catch (GSSException e) {
// skip to next element
}
}
sb.append(']');
return sb.toString();
}
static class SearchKey {
private Oid mechOid = null;
private int usage = GSSCredential.INITIATE_AND_ACCEPT;
public SearchKey(Oid mechOid, int usage) {
this.mechOid = mechOid;
this.usage = usage;
}
public Oid getMech() {
return mechOid;
}
public int getUsage() {
return usage;
}
public boolean equals(Object other) {
if (! (other instanceof SearchKey))
return false;
SearchKey that = (SearchKey) other;
return ((this.mechOid.equals(that.mechOid)) &&
(this.usage == that.usage));
}
public int hashCode() {
return mechOid.hashCode();
}
}
}

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import org.ietf.jgss.*;
/**
* This class helps overcome a limitation of the org.ietf.jgss.GSSException
* class that does not allow the thrower to set a string corresponding to
* the major code.
*/
public class GSSExceptionImpl extends GSSException {
private static final long serialVersionUID = 4251197939069005575L;
private String majorMessage;
/**
* A constructor that takes the majorCode as well as the mech oid that
* will be appended to the standard message defined in its super class.
*/
GSSExceptionImpl(int majorCode, Oid mech) {
super(majorCode);
this.majorMessage = super.getMajorString() + ": " + mech;
}
/**
* A constructor that takes the majorCode as well as the message that
* corresponds to it.
*/
public GSSExceptionImpl(int majorCode, String majorMessage) {
super(majorCode);
this.majorMessage = majorMessage;
}
/**
* A constructor that takes the majorCode and the exception cause.
*/
public GSSExceptionImpl(int majorCode, Exception cause) {
super(majorCode);
initCause(cause);
}
/**
* A constructor that takes the majorCode, the message that
* corresponds to it, and the exception cause.
*/
public GSSExceptionImpl(int majorCode, String majorMessage,
Exception cause) {
this(majorCode, majorMessage);
initCause(cause);
}
/**
* Returns the message that was embedded in this object, otherwise it
* returns the default message that an org.ietf.jgss.GSSException
* generates.
*/
public String getMessage() {
if (majorMessage != null)
return majorMessage;
else
return super.getMessage();
}
}

View file

@ -0,0 +1,345 @@
/*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import org.ietf.jgss.GSSException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import sun.security.util.*;
/**
* This class represents the mechanism independent part of a GSS-API
* context establishment token. Some mechanisms may choose to encode
* all subsequent tokens as well such that they start with an encoding
* of an instance of this class. e.g., The Kerberos v5 GSS-API Mechanism
* uses this header for all GSS-API tokens.
* <p>
* The format is specified in RFC 2743 section 3.1.
*
* @author Mayank Upadhyay
*/
/*
* The RFC states that implementations should explicitly follow the
* encoding scheme descibed in this section rather than use ASN.1
* compilers. However, we should consider removing duplicate ASN.1
* like code from here and depend on sun.security.util if possible.
*/
public class GSSHeader {
private ObjectIdentifier mechOid = null;
private byte[] mechOidBytes = null;
private int mechTokenLength = 0;
/**
* The tag defined in the GSS-API mechanism independent token
* format.
*/
public static final int TOKEN_ID=0x60;
/**
* Creates a GSSHeader instance whose encoding can be used as the
* prefix for a particular mechanism token.
* @param mechOid the Oid of the mechanism which generated the token
* @param mechTokenLength the length of the subsequent portion that
* the mechanism will be adding.
*/
public GSSHeader(ObjectIdentifier mechOid, int mechTokenLength)
throws IOException {
this.mechOid = mechOid;
DerOutputStream temp = new DerOutputStream();
temp.putOID(mechOid);
mechOidBytes = temp.toByteArray();
this.mechTokenLength = mechTokenLength;
}
/**
* Reads in a GSSHeader from an InputStream. Typically this would be
* used as part of reading the complete token from an InputStream
* that is obtained from a socket.
*/
public GSSHeader(InputStream is)
throws IOException, GSSException {
// debug("Parsing GSS token: ");
int tag = is.read();
// debug("tag=" + tag);
if (tag != TOKEN_ID)
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"GSSHeader did not find the right tag");
int length = getLength(is);
DerValue temp = new DerValue(is);
mechOidBytes = temp.toByteArray();
mechOid = temp.getOID();
// debug (" oid=" + mechOid);
// debug (" len starting with oid=" + length);
mechTokenLength = length - mechOidBytes.length;
// debug(" mechToken length=" + mechTokenLength);
}
/**
* Used to obtain the Oid stored in this GSSHeader instance.
* @return the Oid of the mechanism.
*/
public ObjectIdentifier getOid() {
return mechOid;
}
/**
* Used to obtain the length of the mechanism specific token that
* will follow the encoding of this GSSHeader instance.
* @return the length of the mechanism specific token portion that
* will follow this GSSHeader.
*/
public int getMechTokenLength() {
return mechTokenLength;
}
/**
* Used to obtain the length of the encoding of this GSSHeader.
* @return the lenght of the encoding of this GSSHeader instance.
*/
public int getLength() {
int lenField = mechOidBytes.length + mechTokenLength;
return (1 + getLenFieldSize(lenField) + mechOidBytes.length);
}
/**
* Used to determine what the maximum possible mechanism token
* size is if the complete GSSToken returned to the application
* (including a GSSHeader) is not to exceed some pre-determined
* value in size.
* @param mechOid the Oid of the mechanism that will generate
* this GSS-API token
* @param maxTotalSize the pre-determined value that serves as a
* maximum size for the complete GSS-API token (including a
* GSSHeader)
* @return the maximum size of mechanism token that can be used
* so as to not exceed maxTotalSize with the GSS-API token
*/
public static int getMaxMechTokenSize(ObjectIdentifier mechOid,
int maxTotalSize) {
int mechOidBytesSize = 0;
try {
DerOutputStream temp = new DerOutputStream();
temp.putOID(mechOid);
mechOidBytesSize = temp.toByteArray().length;
} catch (IOException e) {
}
// Subtract bytes needed for 0x60 tag and mechOidBytes
maxTotalSize -= (1 + mechOidBytesSize);
// Subtract maximum len bytes
maxTotalSize -= 5;
return maxTotalSize;
/*
* Len field and mechanism token must fit in remaining
* space. The range of the len field that we allow is
* 1 through 5.
*
int mechTokenSize = 0;
for (int lenFieldSize = 1; lenFieldSize <= 5;
lenFieldSize++) {
mechTokenSize = maxTotalSize - lenFieldSize;
if (getLenFieldSize(mechTokenSize + mechOidBytesSize +
lenFieldSize) <= lenFieldSize)
break;
}
return mechTokenSize;
*/
}
/**
* Used to determine the number of bytes that will be need to encode
* the length field of the GSSHeader.
*/
private int getLenFieldSize(int len) {
int retVal = 1;
if (len < 128) {
retVal=1;
} else if (len < (1 << 8)) {
retVal=2;
} else if (len < (1 << 16)) {
retVal=3;
} else if (len < (1 << 24)) {
retVal=4;
} else {
retVal=5; // See getMaxMechTokenSize
}
return retVal;
}
/**
* Encodes this GSSHeader instance onto the provided OutputStream.
* @param os the OutputStream to which the token should be written.
* @return the number of bytes that are output as a result of this
* encoding
*/
public int encode(OutputStream os) throws IOException {
int retVal = 1 + mechOidBytes.length;
os.write(TOKEN_ID);
int length = mechOidBytes.length + mechTokenLength;
retVal += putLength(length, os);
os.write(mechOidBytes);
return retVal;
}
/**
* Get a length from the input stream, allowing for at most 32 bits of
* encoding to be used. (Not the same as getting a tagged integer!)
*
* @return the length or -1 if indefinite length found.
* @exception IOException on parsing error or unsupported lengths.
*/
// shameless lifted from sun.security.util.DerInputStream.
private int getLength(InputStream in) throws IOException {
return getLength(in.read(), in);
}
/**
* Get a length from the input stream, allowing for at most 32 bits of
* encoding to be used. (Not the same as getting a tagged integer!)
*
* @return the length or -1 if indefinite length found.
* @exception IOException on parsing error or unsupported lengths.
*/
// shameless lifted from sun.security.util.DerInputStream.
private int getLength(int lenByte, InputStream in) throws IOException {
int value, tmp;
tmp = lenByte;
if ((tmp & 0x080) == 0x00) { // short form, 1 byte datum
value = tmp;
} else { // long form or indefinite
tmp &= 0x07f;
/*
* NOTE: tmp == 0 indicates indefinite length encoded data.
* tmp > 4 indicates more than 4Gb of data.
*/
if (tmp == 0)
return -1;
if (tmp < 0 || tmp > 4)
throw new IOException("DerInputStream.getLength(): lengthTag="
+ tmp + ", "
+ ((tmp < 0) ? "incorrect DER encoding." : "too big."));
for (value = 0; tmp > 0; tmp --) {
value <<= 8;
value += 0x0ff & in.read();
}
if (value < 0) {
throw new IOException("Invalid length bytes");
}
}
return value;
}
/**
* Put the encoding of the length in the specified stream.
*
* @params len the length of the attribute.
* @param out the outputstream to write the length to
* @return the number of bytes written
* @exception IOException on writing errors.
*/
// Shameless lifted from sun.security.util.DerOutputStream.
private int putLength(int len, OutputStream out) throws IOException {
int retVal = 0;
if (len < 128) {
out.write((byte)len);
retVal=1;
} else if (len < (1 << 8)) {
out.write((byte)0x081);
out.write((byte)len);
retVal=2;
} else if (len < (1 << 16)) {
out.write((byte)0x082);
out.write((byte)(len >> 8));
out.write((byte)len);
retVal=3;
} else if (len < (1 << 24)) {
out.write((byte)0x083);
out.write((byte)(len >> 16));
out.write((byte)(len >> 8));
out.write((byte)len);
retVal=4;
} else {
out.write((byte)0x084);
out.write((byte)(len >> 24));
out.write((byte)(len >> 16));
out.write((byte)(len >> 8));
out.write((byte)len);
retVal=5;
}
return retVal;
}
// XXX Call these two in some central class
private void debug(String str) {
System.err.print(str);
}
private String getHexBytes(byte[] bytes, int len)
throws IOException {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i++) {
int b1 = (bytes[i]>>4) & 0x0f;
int b2 = bytes[i] & 0x0f;
sb.append(Integer.toHexString(b1));
sb.append(Integer.toHexString(b2));
sb.append(' ');
}
return sb.toString();
}
}

View file

@ -0,0 +1,276 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import java.security.Provider;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* This class provides the default implementation of the GSSManager
* interface.
*/
public class GSSManagerImpl extends GSSManager {
// Undocumented property
private static final String USE_NATIVE_PROP =
"sun.security.jgss.native";
private static final Boolean USE_NATIVE;
static {
USE_NATIVE =
AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
String osname = System.getProperty("os.name");
if (osname.startsWith("SunOS") ||
osname.contains("OS X") ||
osname.startsWith("Linux")) {
return Boolean.valueOf(System.getProperty
(USE_NATIVE_PROP));
}
return Boolean.FALSE;
}
});
}
private ProviderList list;
// Used by java SPNEGO impl to make sure native is disabled
public GSSManagerImpl(GSSCaller caller, boolean useNative) {
list = new ProviderList(caller, useNative);
}
// Used by HTTP/SPNEGO NegotiatorImpl
public GSSManagerImpl(GSSCaller caller) {
list = new ProviderList(caller, USE_NATIVE);
}
public GSSManagerImpl() {
list = new ProviderList(GSSCaller.CALLER_UNKNOWN, USE_NATIVE);
}
public Oid[] getMechs(){
return list.getMechs();
}
public Oid[] getNamesForMech(Oid mech)
throws GSSException {
MechanismFactory factory = list.getMechFactory(mech);
return factory.getNameTypes().clone();
}
public Oid[] getMechsForName(Oid nameType){
Oid[] mechs = list.getMechs();
Oid[] retVal = new Oid[mechs.length];
int pos = 0;
// Compatibility with RFC 2853 old NT_HOSTBASED_SERVICE value.
if (nameType.equals(GSSNameImpl.oldHostbasedServiceName)) {
nameType = GSSName.NT_HOSTBASED_SERVICE;
}
// Iterate thru all mechs in GSS
for (int i = 0; i < mechs.length; i++) {
// what nametypes does this mech support?
Oid mech = mechs[i];
try {
Oid[] namesForMech = getNamesForMech(mech);
// Is the desired Oid present in that list?
if (nameType.containedIn(namesForMech)) {
retVal[pos++] = mech;
}
} catch (GSSException e) {
// Squelch it and just skip over this mechanism
GSSUtil.debug("Skip " + mech +
": error retrieving supported name types");
}
}
// Trim the list if needed
if (pos < retVal.length) {
Oid[] temp = new Oid[pos];
for (int i = 0; i < pos; i++)
temp[i] = retVal[i];
retVal = temp;
}
return retVal;
}
public GSSName createName(String nameStr, Oid nameType)
throws GSSException {
return new GSSNameImpl(this, nameStr, nameType);
}
public GSSName createName(byte[] name, Oid nameType)
throws GSSException {
return new GSSNameImpl(this, name, nameType);
}
public GSSName createName(String nameStr, Oid nameType,
Oid mech) throws GSSException {
return new GSSNameImpl(this, nameStr, nameType, mech);
}
public GSSName createName(byte[] name, Oid nameType, Oid mech)
throws GSSException {
return new GSSNameImpl(this, name, nameType, mech);
}
public GSSCredential createCredential(int usage)
throws GSSException {
return wrap(new GSSCredentialImpl(this, usage));
}
public GSSCredential createCredential(GSSName aName,
int lifetime, Oid mech, int usage)
throws GSSException {
return wrap(new GSSCredentialImpl(this, aName, lifetime, mech, usage));
}
public GSSCredential createCredential(GSSName aName,
int lifetime, Oid[] mechs, int usage)
throws GSSException {
return wrap(new GSSCredentialImpl(this, aName, lifetime, mechs, usage));
}
public GSSContext createContext(GSSName peer, Oid mech,
GSSCredential myCred, int lifetime)
throws GSSException {
return wrap(new GSSContextImpl(this, peer, mech, myCred, lifetime));
}
public GSSContext createContext(GSSCredential myCred)
throws GSSException {
return wrap(new GSSContextImpl(this, myCred));
}
public GSSContext createContext(byte[] interProcessToken)
throws GSSException {
return wrap(new GSSContextImpl(this, interProcessToken));
}
public void addProviderAtFront(Provider p, Oid mech)
throws GSSException {
list.addProviderAtFront(p, mech);
}
public void addProviderAtEnd(Provider p, Oid mech)
throws GSSException {
list.addProviderAtEnd(p, mech);
}
public GSSCredentialSpi getCredentialElement(GSSNameSpi name, int initLifetime,
int acceptLifetime, Oid mech, int usage)
throws GSSException {
MechanismFactory factory = list.getMechFactory(mech);
return factory.getCredentialElement(name, initLifetime,
acceptLifetime, usage);
}
// Used by java SPNEGO impl
public GSSNameSpi getNameElement(String name, Oid nameType, Oid mech)
throws GSSException {
// Just use the most preferred MF impl assuming GSSNameSpi
// objects are interoperable among providers
MechanismFactory factory = list.getMechFactory(mech);
return factory.getNameElement(name, nameType);
}
// Used by java SPNEGO impl
public GSSNameSpi getNameElement(byte[] name, Oid nameType, Oid mech)
throws GSSException {
// Just use the most preferred MF impl assuming GSSNameSpi
// objects are interoperable among providers
MechanismFactory factory = list.getMechFactory(mech);
return factory.getNameElement(name, nameType);
}
GSSContextSpi getMechanismContext(GSSNameSpi peer,
GSSCredentialSpi myInitiatorCred,
int lifetime, Oid mech)
throws GSSException {
Provider p = null;
if (myInitiatorCred != null) {
p = myInitiatorCred.getProvider();
}
MechanismFactory factory = list.getMechFactory(mech, p);
return factory.getMechanismContext(peer, myInitiatorCred, lifetime);
}
GSSContextSpi getMechanismContext(GSSCredentialSpi myAcceptorCred,
Oid mech)
throws GSSException {
Provider p = null;
if (myAcceptorCred != null) {
p = myAcceptorCred.getProvider();
}
MechanismFactory factory = list.getMechFactory(mech, p);
return factory.getMechanismContext(myAcceptorCred);
}
GSSContextSpi getMechanismContext(byte[] exportedContext)
throws GSSException {
if ((exportedContext == null) || (exportedContext.length == 0)) {
throw new GSSException(GSSException.NO_CONTEXT);
}
GSSContextSpi result = null;
// Only allow context import with native provider since JGSS
// still has not defined its own interprocess token format
Oid[] mechs = list.getMechs();
for (int i = 0; i < mechs.length; i++) {
MechanismFactory factory = list.getMechFactory(mechs[i]);
if (factory.getProvider().getName().equals("SunNativeGSS")) {
result = factory.getMechanismContext(exportedContext);
if (result != null) break;
}
}
if (result == null) {
throw new GSSException(GSSException.UNAVAILABLE);
}
return result;
}
static {
// Load the extended JGSS interfaces if exist
try {
Class.forName("com.sun.security.jgss.Extender");
} catch (Exception e) {
}
}
static GSSCredential wrap(GSSCredentialImpl cred) {
return sun.security.jgss.JgssExtender.getExtender().wrap(cred);
}
static GSSContext wrap(GSSContextImpl ctxt) {
return sun.security.jgss.JgssExtender.getExtender().wrap(ctxt);
}
}

View file

@ -0,0 +1,508 @@
/*
* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import java.util.Set;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Arrays;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import sun.security.util.ObjectIdentifier;
import sun.security.util.DerInputStream;
import sun.security.util.DerOutputStream;
/**
* This is the implementation class for GSSName. Conceptually the
* GSSName is a container with mechanism specific name elements. Each
* name element is a representation of how that particular mechanism
* would canonicalize this principal.
*
* Generally a GSSName is created by an application when it supplies
* a sequence of bytes and a nametype that helps each mechanism
* decide how to interpret those bytes.
*
* It is not necessary to create name elements for each available
* mechanism at the time the application creates the GSSName. This
* implementation does this lazily, as and when name elements for
* mechanisms are required to be handed out. (Generally, other GSS
* classes like GSSContext and GSSCredential request specific
* elements depending on the mechanisms that they are dealing with.)
* Assume that getting a mechanism to parse the applciation specified
* bytes is an expensive call.
*
* When a GSSName is canonicalized wrt some mechanism, it is supposed
* to discard all elements of other mechanisms and retain only the
* element for this mechanism. In GSS terminology this is called a
* Mechanism Name or MN. This implementation tries to retain the
* application provided bytes and name type just in case the MN is
* asked to produce an element for a mechanism that is different.
*
* When a GSSName is to be exported, the name element for the desired
* mechanism is converted to a byte representation and written
* out. It might happen that a name element for that mechanism cannot
* be obtained. This happens when the mechanism is just not supported
* in this GSS-API or when the mechanism is supported but bytes
* corresponding to the nametypes that it understands are not
* available in this GSSName.
*
* This class is safe for sharing. Each retrieval of a name element
* from getElement() might potentially add a new element to the
* hashmap of elements, but getElement() is synchronized.
*
* @author Mayank Upadhyay
* @since 1.4
*/
public class GSSNameImpl implements GSSName {
/**
* The old Oid used in RFC 2853. Now supported as
* input parameters in:
*
* 1. The four overloaded GSSManager.createName(*) methods
* 2. GSSManager.getMechsForName(Oid)
*
* Note that even if a GSSName is created with this old Oid,
* its internal name type and getStringNameType() output are
* always the new value.
*/
final static Oid oldHostbasedServiceName;
static {
Oid tmp = null;
try {
tmp = new Oid("1.3.6.1.5.6.2");
} catch (Exception e) {
// should never happen
}
oldHostbasedServiceName = tmp;
}
private GSSManagerImpl gssManager = null;
/*
* Store whatever the application passed in. We will use this to
* get individual mechanisms to create name elements as and when
* needed.
* Store both the String and the byte[]. Leave I18N to the
* mechanism by allowing it to extract bytes from the String!
*/
private String appNameStr = null;
private byte[] appNameBytes = null;
private Oid appNameType = null;
/*
* When we figure out what the printable name would be, we store
* both the name and its type.
*/
private String printableName = null;
private Oid printableNameType = null;
private HashMap<Oid, GSSNameSpi> elements = null;
private GSSNameSpi mechElement = null;
static GSSNameImpl wrapElement(GSSManagerImpl gssManager,
GSSNameSpi mechElement) throws GSSException {
return (mechElement == null ?
null : new GSSNameImpl(gssManager, mechElement));
}
GSSNameImpl(GSSManagerImpl gssManager, GSSNameSpi mechElement) {
this.gssManager = gssManager;
appNameStr = printableName = mechElement.toString();
appNameType = printableNameType = mechElement.getStringNameType();
this.mechElement = mechElement;
elements = new HashMap<Oid, GSSNameSpi>(1);
elements.put(mechElement.getMechanism(), this.mechElement);
}
GSSNameImpl(GSSManagerImpl gssManager,
Object appName,
Oid appNameType)
throws GSSException {
this(gssManager, appName, appNameType, null);
}
GSSNameImpl(GSSManagerImpl gssManager,
Object appName,
Oid appNameType,
Oid mech)
throws GSSException {
if (oldHostbasedServiceName.equals(appNameType)) {
appNameType = GSSName.NT_HOSTBASED_SERVICE;
}
if (appName == null)
throw new GSSExceptionImpl(GSSException.BAD_NAME,
"Cannot import null name");
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
if (NT_EXPORT_NAME.equals(appNameType)) {
importName(gssManager, appName);
} else {
init(gssManager, appName, appNameType, mech);
}
}
private void init(GSSManagerImpl gssManager,
Object appName, Oid appNameType,
Oid mech)
throws GSSException {
this.gssManager = gssManager;
this.elements =
new HashMap<Oid, GSSNameSpi>(gssManager.getMechs().length);
if (appName instanceof String) {
this.appNameStr = (String) appName;
/*
* If appNameType is null, then the nametype for this printable
* string is determined only by interrogating the
* mechanism. Thus, defer the setting of printableName and
* printableNameType till later.
*/
if (appNameType != null) {
printableName = appNameStr;
printableNameType = appNameType;
}
} else {
this.appNameBytes = (byte[]) appName;
}
this.appNameType = appNameType;
mechElement = getElement(mech);
/*
* printableName will be null if appName was in a byte[] or if
* appName was in a String but appNameType was null.
*/
if (printableName == null) {
printableName = mechElement.toString();
printableNameType = mechElement.getStringNameType();
}
/*
* At this point the GSSNameImpl has the following set:
* appNameStr or appNameBytes
* appNameType (could be null)
* printableName
* printableNameType
* mechElement (which also exists in the hashmap of elements)
*/
}
private void importName(GSSManagerImpl gssManager,
Object appName)
throws GSSException {
int pos = 0;
byte[] bytes = null;
if (appName instanceof String) {
try {
bytes = ((String) appName).getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
// Won't happen
}
} else
bytes = (byte[]) appName;
if ((bytes[pos++] != 0x04) ||
(bytes[pos++] != 0x01))
throw new GSSExceptionImpl(GSSException.BAD_NAME,
"Exported name token id is corrupted!");
int oidLen = (((0xFF & bytes[pos++]) << 8) |
(0xFF & bytes[pos++]));
ObjectIdentifier temp = null;
try {
DerInputStream din = new DerInputStream(bytes, pos,
oidLen);
temp = new ObjectIdentifier(din);
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.BAD_NAME,
"Exported name Object identifier is corrupted!");
}
Oid oid = new Oid(temp.toString());
pos += oidLen;
int mechPortionLen = (((0xFF & bytes[pos++]) << 24) |
((0xFF & bytes[pos++]) << 16) |
((0xFF & bytes[pos++]) << 8) |
(0xFF & bytes[pos++]));
if (mechPortionLen < 0 || pos > bytes.length - mechPortionLen) {
throw new GSSExceptionImpl(GSSException.BAD_NAME,
"Exported name mech name is corrupted!");
}
byte[] mechPortion = new byte[mechPortionLen];
System.arraycopy(bytes, pos, mechPortion, 0, mechPortionLen);
init(gssManager, mechPortion, NT_EXPORT_NAME, oid);
}
public GSSName canonicalize(Oid mech) throws GSSException {
if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
return wrapElement(gssManager, getElement(mech));
}
/**
* This method may return false negatives. But if it says two
* names are equals, then there is some mechanism that
* authenticates them as the same principal.
*/
public boolean equals(GSSName other) throws GSSException {
if (this.isAnonymous() || other.isAnonymous())
return false;
if (other == this)
return true;
if (! (other instanceof GSSNameImpl))
return equals(gssManager.createName(other.toString(),
other.getStringNameType()));
/*
* XXX Do a comparison of the appNameStr/appNameBytes if
* available. If that fails, then proceed with this test.
*/
GSSNameImpl that = (GSSNameImpl) other;
GSSNameSpi myElement = this.mechElement;
GSSNameSpi element = that.mechElement;
/*
* XXX If they are not of the same mechanism type, convert both to
* Kerberos since it is guaranteed to be present.
*/
if ((myElement == null) && (element != null)) {
myElement = this.getElement(element.getMechanism());
} else if ((myElement != null) && (element == null)) {
element = that.getElement(myElement.getMechanism());
}
if (myElement != null && element != null) {
return myElement.equals(element);
}
if ((this.appNameType != null) &&
(that.appNameType != null)) {
if (!this.appNameType.equals(that.appNameType)) {
return false;
}
byte[] myBytes = null;
byte[] bytes = null;
try {
myBytes =
(this.appNameStr != null ?
this.appNameStr.getBytes("UTF-8") :
this.appNameBytes);
bytes =
(that.appNameStr != null ?
that.appNameStr.getBytes("UTF-8") :
that.appNameBytes);
} catch (UnsupportedEncodingException e) {
// Won't happen
}
return Arrays.equals(myBytes, bytes);
}
return false;
}
/**
* Returns a hashcode value for this GSSName.
*
* @return a hashCode value
*/
public int hashCode() {
/*
* XXX
* In order to get this to work reliably and properly(!), obtain a
* Kerberos name element for the name and then call hashCode on its
* string representation. But this cannot be done if the nametype
* is not one of those supported by the Kerberos provider and hence
* this name cannot be imported by Kerberos. In that case return a
* constant value!
*/
return 1;
}
public boolean equals(Object another) {
try {
// XXX This can lead to an infinite loop. Extract info
// and create a GSSNameImpl with it.
if (another instanceof GSSName)
return equals((GSSName) another);
} catch (GSSException e) {
// Squelch it and return false
}
return false;
}
/**
* Returns a flat name representation for this object. The name
* format is defined in RFC 2743:
*<pre>
* Length Name Description
* 2 TOK_ID Token Identifier
* For exported name objects, this
* must be hex 04 01.
* 2 MECH_OID_LEN Length of the Mechanism OID
* MECH_OID_LEN MECH_OID Mechanism OID, in DER
* 4 NAME_LEN Length of name
* NAME_LEN NAME Exported name; format defined in
* applicable mechanism draft.
*</pre>
*
* Note that it is not required to canonicalize a name before
* calling export(). i.e., the name need not be an MN. If it is
* not an MN, an implementation defined algorithm can be used for
* choosing the mechanism which should export this name.
*
* @return the flat name representation for this object
* @exception GSSException with major codes NAME_NOT_MN, BAD_NAME,
* BAD_NAME, FAILURE.
*/
public byte[] export() throws GSSException {
if (mechElement == null) {
/* Use default mech */
mechElement = getElement(ProviderList.DEFAULT_MECH_OID);
}
byte[] mechPortion = mechElement.export();
byte[] oidBytes = null;
ObjectIdentifier oid = null;
try {
oid = new ObjectIdentifier
(mechElement.getMechanism().toString());
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.FAILURE,
"Invalid OID String ");
}
DerOutputStream dout = new DerOutputStream();
try {
dout.putOID(oid);
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.FAILURE,
"Could not ASN.1 Encode "
+ oid.toString());
}
oidBytes = dout.toByteArray();
byte[] retVal = new byte[2
+ 2 + oidBytes.length
+ 4 + mechPortion.length];
int pos = 0;
retVal[pos++] = 0x04;
retVal[pos++] = 0x01;
retVal[pos++] = (byte) (oidBytes.length>>>8);
retVal[pos++] = (byte) oidBytes.length;
System.arraycopy(oidBytes, 0, retVal, pos, oidBytes.length);
pos += oidBytes.length;
retVal[pos++] = (byte) (mechPortion.length>>>24);
retVal[pos++] = (byte) (mechPortion.length>>>16);
retVal[pos++] = (byte) (mechPortion.length>>>8);
retVal[pos++] = (byte) mechPortion.length;
System.arraycopy(mechPortion, 0, retVal, pos, mechPortion.length);
return retVal;
}
public String toString() {
return printableName;
}
public Oid getStringNameType() throws GSSException {
return printableNameType;
}
public boolean isAnonymous() {
if (printableNameType == null) {
return false;
} else {
return GSSName.NT_ANONYMOUS.equals(printableNameType);
}
}
public boolean isMN() {
return true; // Since always canonicalized for some mech
}
public synchronized GSSNameSpi getElement(Oid mechOid)
throws GSSException {
GSSNameSpi retVal = elements.get(mechOid);
if (retVal == null) {
if (appNameStr != null) {
retVal = gssManager.getNameElement
(appNameStr, appNameType, mechOid);
} else {
retVal = gssManager.getNameElement
(appNameBytes, appNameType, mechOid);
}
elements.put(mechOid, retVal);
}
return retVal;
}
Set<GSSNameSpi> getElements() {
return new HashSet<GSSNameSpi>(elements.values());
}
private static String getNameTypeStr(Oid nameTypeOid) {
if (nameTypeOid == null)
return "(NT is null)";
if (nameTypeOid.equals(NT_USER_NAME))
return "NT_USER_NAME";
if (nameTypeOid.equals(NT_HOSTBASED_SERVICE))
return "NT_HOSTBASED_SERVICE";
if (nameTypeOid.equals(NT_EXPORT_NAME))
return "NT_EXPORT_NAME";
if (nameTypeOid.equals(GSSUtil.NT_GSS_KRB5_PRINCIPAL))
return "NT_GSS_KRB5_PRINCIPAL";
else
return "Unknown";
}
}

View file

@ -0,0 +1,231 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.EOFException;
import sun.security.util.*;
/**
* Utilities for processing GSS Tokens.
*
*/
public abstract class GSSToken {
/**
* Copies an integer value to a byte array in little endian form.
* @param value the integer value to write
* @param array the byte array into which the integer must be copied. It
* is assumed that the array will be large enough to hold the 4 bytes of
* the integer.
*/
public static final void writeLittleEndian(int value, byte[] array) {
writeLittleEndian(value, array, 0);
}
/**
* Copies an integer value to a byte array in little endian form.
* @param value the integer value to write
* @param array the byte array into which the integer must be copied. It
* is assumed that the array will be large enough to hold the 4 bytes of
* the integer.
* @param pos the position at which to start writing
*/
public static final void writeLittleEndian(int value, byte[] array,
int pos) {
array[pos++] = (byte)(value);
array[pos++] = (byte)((value>>>8));
array[pos++] = (byte)((value>>>16));
array[pos++] = (byte)((value>>>24));
}
public static final void writeBigEndian(int value, byte[] array) {
writeBigEndian(value, array, 0);
}
public static final void writeBigEndian(int value, byte[] array,
int pos) {
array[pos++] = (byte)((value>>>24));
array[pos++] = (byte)((value>>>16));
array[pos++] = (byte)((value>>>8));
array[pos++] = (byte)(value);
}
/**
* Reads an integer value from a byte array in little endian form. This
* method allows the reading of two byte values as well as four bytes
* values both of which are needed in the Kerberos v5 GSS-API mechanism.
*
* @param data the array containing the bytes of the integer value
* @param pos the offset in the array
* @param size the number of bytes to read from the array.
* @return the integer value
*/
public static final int readLittleEndian(byte[] data, int pos, int size) {
int retVal = 0;
int shifter = 0;
while (size > 0) {
retVal += (data[pos] & 0xff) << shifter;
shifter += 8;
pos++;
size--;
}
return retVal;
}
public static final int readBigEndian(byte[] data, int pos, int size) {
int retVal = 0;
int shifter = (size-1)*8;
while (size > 0) {
retVal += (data[pos] & 0xff) << shifter;
shifter -= 8;
pos++;
size--;
}
return retVal;
}
/**
* Writes a two byte integer value to a OutputStream.
*
* @param val the integer value. It will lose the high-order two bytes.
* @param os the OutputStream to write to
* @throws IOException if an error occurs while writing to the OutputStream
*/
public static final void writeInt(int val, OutputStream os)
throws IOException {
os.write(val>>>8);
os.write(val);
}
/**
* Writes a two byte integer value to a byte array.
*
* @param val the integer value. It will lose the high-order two bytes.
* @param dest the byte array to write to
* @param pos the offset to start writing to
*/
public static final int writeInt(int val, byte[] dest, int pos) {
dest[pos++] = (byte)(val>>>8);
dest[pos++] = (byte)val;
return pos;
}
/**
* Reads a two byte integer value from an InputStream.
*
* @param is the InputStream to read from
* @return the integer value
* @throws IOException if some errors occurs while reading the integer
* bytes.
*/
public static final int readInt(InputStream is) throws IOException {
return (((0xFF & is.read()) << 8)
| (0xFF & is.read()));
}
/**
* Reads a two byte integer value from a byte array.
*
* @param src the byte arra to read from
* @param pos the offset to start reading from
* @return the integer value
*/
public static final int readInt(byte[] src, int pos) {
return ((0xFF & src[pos])<<8 | (0xFF & src[pos+1]));
}
/**
* Blocks till the required number of bytes have been read from the
* input stream.
*
* @param is the InputStream to read from
* @param buffer the buffer to store the bytes into
* @throws EOFException if EOF is reached before all bytes are
* read.
* @throws IOException is an error occurs while reading
*/
public static final void readFully(InputStream is, byte[] buffer)
throws IOException {
readFully(is, buffer, 0, buffer.length);
}
/**
* Blocks till the required number of bytes have been read from the
* input stream.
*
* @param is the InputStream to read from
* @param buffer the buffer to store the bytes into
* @param offset the offset to start storing at
* @param len the number of bytes to read
* @throws EOFException if EOF is reached before all bytes are
* read.
* @throws IOException is an error occurs while reading
*/
public static final void readFully(InputStream is,
byte[] buffer, int offset, int len)
throws IOException {
int temp;
while (len > 0) {
temp = is.read(buffer, offset, len);
if (temp == -1)
throw new EOFException("Cannot read all "
+ len
+ " bytes needed to form this token!");
offset += temp;
len -= temp;
}
}
public static final void debug(String str) {
System.err.print(str);
}
public static final String getHexBytes(byte[] bytes) {
return getHexBytes(bytes, 0, bytes.length);
}
public static final String getHexBytes(byte[] bytes, int len) {
return getHexBytes(bytes, 0, len);
}
public static final String getHexBytes(byte[] bytes, int pos, int len) {
StringBuilder sb = new StringBuilder();
for (int i = pos; i < (pos+len); i++) {
int b1 = (bytes[i]>>4) & 0x0f;
int b2 = bytes[i] & 0x0f;
sb.append(Integer.toHexString(b1));
sb.append(Integer.toHexString(b2));
sb.append(' ');
}
return sb.toString();
}
}

View file

@ -0,0 +1,373 @@
/*
* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KerberosKey;
import org.ietf.jgss.*;
import sun.security.jgss.spi.GSSNameSpi;
import sun.security.jgss.spi.GSSCredentialSpi;
import sun.security.action.GetPropertyAction;
import sun.security.jgss.krb5.Krb5NameElement;
import sun.security.jgss.spnego.SpNegoCredElement;
import java.util.Set;
import java.util.HashSet;
import java.util.Vector;
import java.util.Iterator;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import sun.security.action.GetBooleanAction;
import sun.security.util.ConsoleCallbackHandler;
/**
* The GSSUtilImplementation that knows how to work with the internals of
* the GSS-API.
*/
public class GSSUtil {
public static final Oid GSS_KRB5_MECH_OID =
GSSUtil.createOid("1.2.840.113554.1.2.2");
public static final Oid GSS_KRB5_MECH_OID2 =
GSSUtil.createOid("1.3.5.1.5.2");
public static final Oid GSS_KRB5_MECH_OID_MS =
GSSUtil.createOid("1.2.840.48018.1.2.2");
public static final Oid GSS_SPNEGO_MECH_OID =
GSSUtil.createOid("1.3.6.1.5.5.2");
public static final Oid NT_GSS_KRB5_PRINCIPAL =
GSSUtil.createOid("1.2.840.113554.1.2.2.1");
private static final String DEFAULT_HANDLER =
"auth.login.defaultCallbackHandler";
static final boolean DEBUG;
static {
DEBUG = (AccessController.doPrivileged
(new GetBooleanAction("sun.security.jgss.debug"))).
booleanValue();
}
static void debug(String message) {
if (DEBUG) {
assert(message != null);
System.out.println(message);
}
}
// NOTE: this method is only for creating Oid objects with
// known to be valid <code>oidStr</code> given it ignores
// the GSSException
public static Oid createOid(String oidStr) {
try {
return new Oid(oidStr);
} catch (GSSException e) {
debug("Ignored invalid OID: " + oidStr);
return null;
}
}
public static boolean isSpNegoMech(Oid oid) {
return (GSS_SPNEGO_MECH_OID.equals(oid));
}
public static boolean isKerberosMech(Oid oid) {
return (GSS_KRB5_MECH_OID.equals(oid) ||
GSS_KRB5_MECH_OID2.equals(oid) ||
GSS_KRB5_MECH_OID_MS.equals(oid));
}
public static String getMechStr(Oid oid) {
if (isSpNegoMech(oid)) {
return "SPNEGO";
} else if (isKerberosMech(oid)) {
return "Kerberos V5";
} else {
return oid.toString();
}
}
/**
* Note: The current impl only works with Sun's impl of
* GSSName and GSSCredential since it depends on package
* private APIs.
*/
public static Subject getSubject(GSSName name,
GSSCredential creds) {
HashSet<Object> privCredentials = null;
HashSet<Object> pubCredentials = new HashSet<Object>(); // empty Set
Set<GSSCredentialSpi> gssCredentials = null;
Set<KerberosPrincipal> krb5Principals =
new HashSet<KerberosPrincipal>();
if (name instanceof GSSNameImpl) {
try {
GSSNameSpi ne = ((GSSNameImpl) name).getElement
(GSS_KRB5_MECH_OID);
String krbName = ne.toString();
if (ne instanceof Krb5NameElement) {
krbName =
((Krb5NameElement) ne).getKrb5PrincipalName().getName();
}
KerberosPrincipal krbPrinc = new KerberosPrincipal(krbName);
krb5Principals.add(krbPrinc);
} catch (GSSException ge) {
debug("Skipped name " + name + " due to " + ge);
}
}
if (creds instanceof GSSCredentialImpl) {
gssCredentials = ((GSSCredentialImpl) creds).getElements();
privCredentials = new HashSet<Object>(gssCredentials.size());
populateCredentials(privCredentials, gssCredentials);
} else {
privCredentials = new HashSet<Object>(); // empty Set
}
debug("Created Subject with the following");
debug("principals=" + krb5Principals);
debug("public creds=" + pubCredentials);
debug("private creds=" + privCredentials);
return new Subject(false, krb5Principals, pubCredentials,
privCredentials);
}
/**
* Populates the set credentials with elements from gssCredentials. At
* the same time, it converts any subclasses of KerberosTicket
* into KerberosTicket instances and any subclasses of KerberosKey into
* KerberosKey instances. (It is not desirable to expose the customer
* to sun.security.jgss.krb5.Krb5InitCredential which extends
* KerberosTicket and sun.security.jgss.krb5.Kbr5AcceptCredential which
* extends KerberosKey.)
*/
private static void populateCredentials(Set<Object> credentials,
Set<?> gssCredentials) {
Object cred;
Iterator<?> elements = gssCredentials.iterator();
while (elements.hasNext()) {
cred = elements.next();
// Retrieve the internal cred out of SpNegoCredElement
if (cred instanceof SpNegoCredElement) {
cred = ((SpNegoCredElement) cred).getInternalCred();
}
if (cred instanceof KerberosTicket) {
if (!cred.getClass().getName().equals
("javax.security.auth.kerberos.KerberosTicket")) {
KerberosTicket tempTkt = (KerberosTicket) cred;
cred = new KerberosTicket(tempTkt.getEncoded(),
tempTkt.getClient(),
tempTkt.getServer(),
tempTkt.getSessionKey().getEncoded(),
tempTkt.getSessionKeyType(),
tempTkt.getFlags(),
tempTkt.getAuthTime(),
tempTkt.getStartTime(),
tempTkt.getEndTime(),
tempTkt.getRenewTill(),
tempTkt.getClientAddresses());
}
credentials.add(cred);
} else if (cred instanceof KerberosKey) {
if (!cred.getClass().getName().equals
("javax.security.auth.kerberos.KerberosKey")) {
KerberosKey tempKey = (KerberosKey) cred;
cred = new KerberosKey(tempKey.getPrincipal(),
tempKey.getEncoded(),
tempKey.getKeyType(),
tempKey.getVersionNumber());
}
credentials.add(cred);
} else {
// Ignore non-KerberosTicket and non-KerberosKey elements
debug("Skipped cred element: " + cred);
}
}
}
/**
* Authenticate using the login module from the specified
* configuration entry.
*
* @param caller the caller of JAAS Login
* @param mech the mech to be used
* @return the authenticated subject
*/
public static Subject login(GSSCaller caller, Oid mech) throws LoginException {
CallbackHandler cb = null;
if (caller instanceof HttpCaller) {
cb = new sun.net.www.protocol.http.spnego.NegotiateCallbackHandler(
((HttpCaller)caller).info());
} else {
String defaultHandler =
java.security.Security.getProperty(DEFAULT_HANDLER);
// get the default callback handler
if ((defaultHandler != null) && (defaultHandler.length() != 0)) {
cb = null;
} else {
cb = new ConsoleCallbackHandler();
}
}
// New instance of LoginConfigImpl must be created for each login,
// since the entry name is not passed as the first argument, but
// generated with caller and mech inside LoginConfigImpl
LoginContext lc = new LoginContext("", null, cb,
new LoginConfigImpl(caller, mech));
lc.login();
return lc.getSubject();
}
/**
* Determines if the application doesn't mind if the mechanism obtains
* the required credentials from outside of the current Subject. Our
* Kerberos v5 mechanism would do a JAAS login on behalf of the
* application if this were the case.
*
* The application indicates this by explicitly setting the system
* property javax.security.auth.useSubjectCredsOnly to false.
*/
public static boolean useSubjectCredsOnly(GSSCaller caller) {
// HTTP/SPNEGO doesn't use the standard JAAS framework. Instead, it
// uses the java.net.Authenticator style, therefore always return
// false here.
if (caller instanceof HttpCaller) {
return false;
}
/*
* Don't use GetBooleanAction because the default value in the JRE
* (when this is unset) has to treated as true.
*/
String propValue = AccessController.doPrivileged(
new GetPropertyAction("javax.security.auth.useSubjectCredsOnly",
"true"));
/*
* This property has to be explicitly set to "false". Invalid
* values should be ignored and the default "true" assumed.
*/
return (!propValue.equalsIgnoreCase("false"));
}
/**
* Determines the SPNEGO interoperability mode with Microsoft;
* by default it is set to true.
*
* To disable it, the application indicates this by explicitly setting
* the system property sun.security.spnego.interop to false.
*/
public static boolean useMSInterop() {
/*
* Don't use GetBooleanAction because the default value in the JRE
* (when this is unset) has to treated as true.
*/
String propValue = AccessController.doPrivileged(
new GetPropertyAction("sun.security.spnego.msinterop",
"true"));
/*
* This property has to be explicitly set to "false". Invalid
* values should be ignored and the default "true" assumed.
*/
return (!propValue.equalsIgnoreCase("false"));
}
/**
* Searches the private credentials of current Subject with the
* specified criteria and returns the matching GSSCredentialSpi
* object out of Sun's impl of GSSCredential. Returns null if
* no Subject present or a Vector which contains 0 or more
* matching GSSCredentialSpi objects.
*/
public static <T extends GSSCredentialSpi> Vector<T>
searchSubject(final GSSNameSpi name,
final Oid mech,
final boolean initiate,
final Class<? extends T> credCls) {
debug("Search Subject for " + getMechStr(mech) +
(initiate? " INIT" : " ACCEPT") + " cred (" +
(name == null? "<<DEF>>" : name.toString()) + ", " +
credCls.getName() + ")");
final AccessControlContext acc = AccessController.getContext();
try {
Vector<T> creds =
AccessController.doPrivileged
(new PrivilegedExceptionAction<Vector<T>>() {
public Vector<T> run() throws Exception {
Subject accSubj = Subject.getSubject(acc);
Vector<T> result = null;
if (accSubj != null) {
result = new Vector<T>();
Iterator<GSSCredentialImpl> iterator =
accSubj.getPrivateCredentials
(GSSCredentialImpl.class).iterator();
while (iterator.hasNext()) {
GSSCredentialImpl cred = iterator.next();
debug("...Found cred" + cred);
try {
GSSCredentialSpi ce =
cred.getElement(mech, initiate);
debug("......Found element: " + ce);
if (ce.getClass().equals(credCls) &&
(name == null ||
name.equals((Object) ce.getName()))) {
result.add(credCls.cast(ce));
} else {
debug("......Discard element");
}
} catch (GSSException ge) {
debug("...Discard cred (" + ge + ")");
}
}
} else debug("No Subject");
return result;
}
});
return creds;
} catch (PrivilegedActionException pae) {
debug("Unexpected exception when searching Subject:");
if (DEBUG) pae.printStackTrace();
return null;
}
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import sun.net.www.protocol.http.HttpCallerInfo;
/**
* A special kind of GSSCaller, which origins from HTTP/Negotiate and contains
* info about what triggers the JGSS calls.
*/
public class HttpCaller extends GSSCaller {
final private HttpCallerInfo hci;
public HttpCaller(HttpCallerInfo hci) {
super("HTTP_CLIENT");
this.hci = hci;
}
public HttpCallerInfo info() {
return hci;
}
}

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
/**
* The extending point of basic JGSS-API.
* <p>
* If a module wants to extend basic JGSS-API classes, it should extends this
* class and register itself as "the extender" using the setExtender method.
* When various GSSManager.createXXX methods are called, they will call
* "the extender"'s wrap methods to create objects of extended types
* instead of basic types.
* <p>
* We have only one extension now defined in com.sun.security.jgss, and the
* registering process is triggered in {@link GSSManagerImpl} by calling
* Class.forName("com.sun.security.jgss.Extender"). Only GSSContext
* and GSSCredential are extended now.
* <p>
* The setExtender method should be called before any JGSS call.
*/
public class JgssExtender {
// "The extender"
private static volatile JgssExtender theOne = new JgssExtender();
/**
* Gets "the extender". GSSManager calls this method so that it can
* wrap basic objects into extended objects.
* @return the extender
*/
public static JgssExtender getExtender() {
return theOne;
}
/**
* Set "the extender" so that GSSManager can create extended objects.
*/
protected static void setExtender(JgssExtender theOne) {
JgssExtender.theOne = theOne;
}
/**
* Wraps a plain GSSCredential object into an extended type.
*/
public GSSCredential wrap(GSSCredential cred) {
return cred;
}
/**
* Wraps a plain GSSContext object into an extended type.
*/
public GSSContext wrap(GSSContext ctxt) {
return ctxt;
}
}

View file

@ -0,0 +1,198 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import java.util.HashMap;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import org.ietf.jgss.Oid;
/**
* A Configuration implementation especially designed for JGSS.
*
* @author weijun.wang
* @since 1.6
*/
public class LoginConfigImpl extends Configuration {
private final Configuration config;
private final GSSCaller caller;
private final String mechName;
private static final sun.security.util.Debug debug =
sun.security.util.Debug.getInstance("gssloginconfig", "\t[GSS LoginConfigImpl]");
/**
* A new instance of LoginConfigImpl must be created for each login request
* since it's only used by a single (caller, mech) pair
* @param caller defined in GSSUtil as CALLER_XXX final fields
* @param mech defined in GSSUtil as XXX_MECH_OID final fields
*/
public LoginConfigImpl(GSSCaller caller, Oid mech) {
this.caller = caller;
if (mech.equals(GSSUtil.GSS_KRB5_MECH_OID)) {
mechName = "krb5";
} else {
throw new IllegalArgumentException(mech.toString() + " not supported");
}
config = java.security.AccessController.doPrivileged
(new java.security.PrivilegedAction <Configuration> () {
public Configuration run() {
return Configuration.getConfiguration();
}
});
}
/**
* @param name Almost useless, since the (caller, mech) is already passed
* into constructor. The only use will be detecting OTHER which
* is called in LoginContext
*/
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
AppConfigurationEntry[] entries = null;
// This is the second call from LoginContext, which we will just ignore
if ("OTHER".equalsIgnoreCase(name)) {
return null;
}
String[] alts = null;
// Compatibility:
// For the 4 old callers, old entry names will be used if the new
// entry name is not provided.
if ("krb5".equals(mechName)) {
if (caller == GSSCaller.CALLER_INITIATE) {
alts = new String[] {
"com.sun.security.jgss.krb5.initiate",
"com.sun.security.jgss.initiate",
};
} else if (caller == GSSCaller.CALLER_ACCEPT) {
alts = new String[] {
"com.sun.security.jgss.krb5.accept",
"com.sun.security.jgss.accept",
};
} else if (caller == GSSCaller.CALLER_SSL_CLIENT) {
alts = new String[] {
"com.sun.security.jgss.krb5.initiate",
"com.sun.net.ssl.client",
};
} else if (caller == GSSCaller.CALLER_SSL_SERVER) {
alts = new String[] {
"com.sun.security.jgss.krb5.accept",
"com.sun.net.ssl.server",
};
} else if (caller instanceof HttpCaller) {
alts = new String[] {
"com.sun.security.jgss.krb5.initiate",
};
} else if (caller == GSSCaller.CALLER_UNKNOWN) {
throw new AssertionError("caller not defined");
}
} else {
throw new IllegalArgumentException(mechName + " not supported");
// No other mech at the moment, maybe --
/*
switch (caller) {
case GSSUtil.CALLER_INITIATE:
case GSSUtil.CALLER_SSL_CLIENT:
case GSSUtil.CALLER_HTTP_NEGOTIATE:
alts = new String[] {
"com.sun.security.jgss." + mechName + ".initiate",
};
break;
case GSSUtil.CALLER_ACCEPT:
case GSSUtil.CALLER_SSL_SERVER:
alts = new String[] {
"com.sun.security.jgss." + mechName + ".accept",
};
break;
case GSSUtil.CALLER_UNKNOWN:
// should never use
throw new AssertionError("caller cannot be unknown");
default:
throw new AssertionError("caller not defined");
}
*/
}
for (String alt: alts) {
entries = config.getAppConfigurationEntry(alt);
if (debug != null) {
debug.println("Trying " + alt +
((entries == null)?": does not exist.":": Found!"));
}
if (entries != null) {
break;
}
}
if (entries == null) {
if (debug != null) {
debug.println("Cannot read JGSS entry, use default values instead.");
}
entries = getDefaultConfigurationEntry();
}
return entries;
}
/**
* Default value for a caller-mech pair when no entry is defined in
* the system-wide Configuration object.
*/
private AppConfigurationEntry[] getDefaultConfigurationEntry() {
HashMap <String, String> options = new HashMap <String, String> (2);
if (mechName == null || mechName.equals("krb5")) {
if (isServerSide(caller)) {
// Assuming the keytab file can be found through
// krb5 config file or under user home directory
options.put("useKeyTab", "true");
options.put("storeKey", "true");
options.put("doNotPrompt", "true");
options.put("principal", "*");
options.put("isInitiator", "false");
} else {
options.put("useTicketCache", "true");
options.put("doNotPrompt", "false");
}
return new AppConfigurationEntry[] {
new AppConfigurationEntry(
"com.sun.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
options)
};
}
return null;
}
private static boolean isServerSide (GSSCaller caller) {
return GSSCaller.CALLER_ACCEPT == caller ||
GSSCaller.CALLER_SSL_SERVER == caller;
}
}

View file

@ -0,0 +1,547 @@
/*
* Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import java.lang.reflect.InvocationTargetException;
import org.ietf.jgss.*;
import java.security.AccessController;
import java.security.Provider;
import java.security.Security;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Enumeration;
import java.util.Iterator;
import sun.security.jgss.spi.*;
import sun.security.jgss.wrapper.NativeGSSFactory;
import sun.security.jgss.wrapper.SunNativeProvider;
import sun.security.action.GetPropertyAction;
/**
* This class stores the list of providers that this
* GSS-Implementation is configured to use. The GSSManagerImpl class
* queries this class whenever it needs a mechanism's factory.<p>
*
* This class stores an ordered list of pairs of the form
* {@code <provider, oid>}. When it attempts to instantiate a mechanism
* defined by oid o, it steps through the list looking for an entry
* with oid=o, or with oid=null. (An entry with oid=null matches all
* mechanisms.) When it finds such an entry, the corresponding
* provider is approached for the mechanism's factory class.
* At instantiation time this list in initialized to contain those
* system wide providers that contain a property of the form
* "GssApiMechanism.x.y.z..." where "x.y.z..." is a numeric object
* identifier with numbers x, y, z, etc. Such a property is defined
* to map to that provider's implementation of the MechanismFactory
* interface for the mechanism x.y.z...
* As and when a MechanismFactory is instantiated, it is
* cached for future use. <p>
*
* An application can cause more providers to be added by means of
* the addProviderAtFront and addProviderAtEnd methods on
* GSSManager which get delegated to this class. The
* addProviderAtFront method can also cause a change in the ordering
* of the providers without adding any new providers, by causing a
* provider to move up in a list. The method addProviderAtEnd can
* only add providers at the end of the list if they are not already
* in the list. The rationale is that an application will call
* addProviderAtFront when it wants a provider to be used in
* preference over the default ones. And it will call
* addProviderAtEnd when it wants a provider to be used in case
* the system ones don't suffice.<p>
*
* If a mechanism's factory is being obtained from a provider as a
* result of encountering a entryof the form {@code <provider, oid>} where
* oid is non-null, then the assumption is that the application added
* this entry and it wants this mechanism to be obtained from this
* provider. Thus is the provider does not actually contain the
* requested mechanism, an exception will be thrown. However, if the
* entry were of the form {@code <provider, null>}, then it is viewed more
* liberally and is simply skipped over if the provider does not claim to
* support the requested mechanism.
*/
public final class ProviderList {
private static final String PROV_PROP_PREFIX = "GssApiMechanism.";
private static final int PROV_PROP_PREFIX_LEN =
PROV_PROP_PREFIX.length();
private static final String SPI_MECH_FACTORY_TYPE
= "sun.security.jgss.spi.MechanismFactory";
// Undocumented property?
private static final String DEFAULT_MECH_PROP =
"sun.security.jgss.mechanism";
public static final Oid DEFAULT_MECH_OID;
static {
/*
* Set the default mechanism. Kerberos v5 is the default
* mechanism unless it is overridden by a system property.
* with a valid OID value
*/
Oid defOid = null;
String defaultOidStr = AccessController.doPrivileged
(new GetPropertyAction(DEFAULT_MECH_PROP));
if (defaultOidStr != null) {
defOid = GSSUtil.createOid(defaultOidStr);
}
DEFAULT_MECH_OID =
(defOid == null ? GSSUtil.GSS_KRB5_MECH_OID : defOid);
}
private ArrayList<PreferencesEntry> preferences =
new ArrayList<PreferencesEntry>(5);
private HashMap<PreferencesEntry, MechanismFactory> factories =
new HashMap<PreferencesEntry, MechanismFactory>(5);
private HashSet<Oid> mechs = new HashSet<Oid>(5);
final private GSSCaller caller;
public ProviderList(GSSCaller caller, boolean useNative) {
this.caller = caller;
Provider[] provList;
if (useNative) {
provList = new Provider[1];
provList[0] = new SunNativeProvider();
} else {
provList = Security.getProviders();
}
for (int i = 0; i < provList.length; i++) {
Provider prov = provList[i];
try {
addProviderAtEnd(prov, null);
} catch (GSSException ge) {
// Move on to the next provider
GSSUtil.debug("Error in adding provider " +
prov.getName() + ": " + ge);
}
} // End of for loop
}
/**
* Determines if the given provider property represents a GSS-API
* Oid to MechanismFactory mapping.
* @return true if this is a GSS-API property, false otherwise.
*/
private boolean isMechFactoryProperty(String prop) {
return (prop.startsWith(PROV_PROP_PREFIX) ||
prop.regionMatches(true, 0, // Try ignoring case
PROV_PROP_PREFIX, 0,
PROV_PROP_PREFIX_LEN));
}
private Oid getOidFromMechFactoryProperty(String prop)
throws GSSException {
String oidPart = prop.substring(PROV_PROP_PREFIX_LEN);
return new Oid(oidPart);
}
// So the existing code do not have to be changed
synchronized public MechanismFactory getMechFactory(Oid mechOid)
throws GSSException {
if (mechOid == null) mechOid = ProviderList.DEFAULT_MECH_OID;
return getMechFactory(mechOid, null);
}
/**
* Obtains a MechanismFactory for a given mechanism. If the
* specified provider is not null, then the impl from the
* provider is used. Otherwise, the most preferred impl based
* on the configured preferences is used.
* @param mechOid the oid of the desired mechanism
* @return a MechanismFactory for the desired mechanism.
* @throws GSSException when the specified provider does not
* support the desired mechanism, or when no provider supports
* the desired mechanism.
*/
synchronized public MechanismFactory getMechFactory(Oid mechOid,
Provider p)
throws GSSException {
if (mechOid == null) mechOid = ProviderList.DEFAULT_MECH_OID;
if (p == null) {
// Iterate thru all preferences to find right provider
String className;
PreferencesEntry entry;
Iterator<PreferencesEntry> list = preferences.iterator();
while (list.hasNext()) {
entry = list.next();
if (entry.impliesMechanism(mechOid)) {
MechanismFactory retVal = getMechFactory(entry, mechOid);
if (retVal != null) return retVal;
}
} // end of while loop
throw new GSSExceptionImpl(GSSException.BAD_MECH, mechOid);
} else {
// Use the impl from the specified provider; return null if the
// the mech is unsupported by the specified provider.
PreferencesEntry entry = new PreferencesEntry(p, mechOid);
return getMechFactory(entry, mechOid);
}
}
/**
* Helper routine that uses a preferences entry to obtain an
* implementation of a MechanismFactory from it.
* @param e the preferences entry that contains the provider and
* either a null of an explicit oid that matched the oid of the
* desired mechanism.
* @param mechOid the oid of the desired mechanism
* @throws GSSException If the application explicitly requested
* this entry's provider to be used for the desired mechanism but
* some problem is encountered
*/
private MechanismFactory getMechFactory(PreferencesEntry e, Oid mechOid)
throws GSSException {
Provider p = e.getProvider();
/*
* See if a MechanismFactory was previously instantiated for
* this provider and mechanism combination.
*/
PreferencesEntry searchEntry = new PreferencesEntry(p, mechOid);
MechanismFactory retVal = factories.get(searchEntry);
if (retVal == null) {
/*
* Apparently not. Now try to instantiate this class from
* the provider.
*/
String prop = PROV_PROP_PREFIX + mechOid.toString();
String className = p.getProperty(prop);
if (className != null) {
retVal = getMechFactoryImpl(p, className, mechOid, caller);
factories.put(searchEntry, retVal);
} else {
/*
* This provider does not support this mechanism.
* If the application explicitly requested that
* this provider be used for this mechanism, then
* throw an exception
*/
if (e.getOid() != null) {
throw new GSSExceptionImpl(GSSException.BAD_MECH,
"Provider " + p.getName() +
" does not support mechanism " + mechOid);
}
}
}
return retVal;
}
/**
* Helper routine to obtain a MechanismFactory implementation
* from the same class loader as the provider of this
* implementation.
* @param p the provider whose classloader must be used for
* instantiating the desired MechanismFactory
* @ param className the name of the MechanismFactory class
* @throws GSSException If some error occurs when trying to
* instantiate this MechanismFactory.
*/
private static MechanismFactory getMechFactoryImpl(Provider p,
String className,
Oid mechOid,
GSSCaller caller)
throws GSSException {
try {
Class<?> baseClass = Class.forName(SPI_MECH_FACTORY_TYPE);
/*
* Load the implementation class with the same class loader
* that was used to load the provider.
* In order to get the class loader of a class, the
* caller's class loader must be the same as or an ancestor of
* the class loader being returned. Otherwise, the caller must
* have "getClassLoader" permission, or a SecurityException
* will be thrown.
*/
ClassLoader cl = p.getClass().getClassLoader();
Class<?> implClass;
if (cl != null) {
implClass = cl.loadClass(className);
} else {
implClass = Class.forName(className);
}
if (baseClass.isAssignableFrom(implClass)) {
java.lang.reflect.Constructor<?> c =
implClass.getConstructor(GSSCaller.class);
MechanismFactory mf = (MechanismFactory) (c.newInstance(caller));
if (mf instanceof NativeGSSFactory) {
((NativeGSSFactory) mf).setMech(mechOid);
}
return mf;
} else {
throw createGSSException(p, className, "is not a " +
SPI_MECH_FACTORY_TYPE, null);
}
} catch (ClassNotFoundException e) {
throw createGSSException(p, className, "cannot be created", e);
} catch (NoSuchMethodException e) {
throw createGSSException(p, className, "cannot be created", e);
} catch (InvocationTargetException e) {
throw createGSSException(p, className, "cannot be created", e);
} catch (InstantiationException e) {
throw createGSSException(p, className, "cannot be created", e);
} catch (IllegalAccessException e) {
throw createGSSException(p, className, "cannot be created", e);
} catch (SecurityException e) {
throw createGSSException(p, className, "cannot be created", e);
}
}
// Only used by getMechFactoryImpl
private static GSSException createGSSException(Provider p,
String className,
String trailingMsg,
Exception cause) {
String errClassInfo = className + " configured by " +
p.getName() + " for GSS-API Mechanism Factory ";
return new GSSExceptionImpl(GSSException.BAD_MECH,
errClassInfo + trailingMsg,
cause);
}
public Oid[] getMechs() {
return mechs.toArray(new Oid[] {});
}
synchronized public void addProviderAtFront(Provider p, Oid mechOid)
throws GSSException {
PreferencesEntry newEntry = new PreferencesEntry(p, mechOid);
PreferencesEntry oldEntry;
boolean foundSomeMech;
Iterator<PreferencesEntry> list = preferences.iterator();
while (list.hasNext()) {
oldEntry = list.next();
if (newEntry.implies(oldEntry))
list.remove();
}
if (mechOid == null) {
foundSomeMech = addAllMechsFromProvider(p);
} else {
String oidStr = mechOid.toString();
if (p.getProperty(PROV_PROP_PREFIX + oidStr) == null)
throw new GSSExceptionImpl(GSSException.BAD_MECH,
"Provider " + p.getName()
+ " does not support "
+ oidStr);
mechs.add(mechOid);
foundSomeMech = true;
}
if (foundSomeMech) {
preferences.add(0, newEntry);
}
}
synchronized public void addProviderAtEnd(Provider p, Oid mechOid)
throws GSSException {
PreferencesEntry newEntry = new PreferencesEntry(p, mechOid);
PreferencesEntry oldEntry;
boolean foundSomeMech;
Iterator<PreferencesEntry> list = preferences.iterator();
while (list.hasNext()) {
oldEntry = list.next();
if (oldEntry.implies(newEntry))
return;
}
// System.out.println("addProviderAtEnd: No it is not redundant");
if (mechOid == null)
foundSomeMech = addAllMechsFromProvider(p);
else {
String oidStr = mechOid.toString();
if (p.getProperty(PROV_PROP_PREFIX + oidStr) == null)
throw new GSSExceptionImpl(GSSException.BAD_MECH,
"Provider " + p.getName()
+ " does not support "
+ oidStr);
mechs.add(mechOid);
foundSomeMech = true;
}
if (foundSomeMech) {
preferences.add(newEntry);
}
}
/**
* Helper routine to go through all properties contined in a
* provider and add its mechanisms to the list of supported
* mechanisms. If no default mechanism has been assinged so far,
* it sets the default MechanismFactory and Oid as well.
* @param p the provider to query
* @return true if there is at least one mechanism that this
* provider contributed, false otherwise
*/
private boolean addAllMechsFromProvider(Provider p) {
String prop;
boolean retVal = false;
// Get all props for this provider
Enumeration<Object> props = p.keys();
// See if there are any GSS prop's
while (props.hasMoreElements()) {
prop = (String) props.nextElement();
if (isMechFactoryProperty(prop)) {
// Ok! This is a GSS provider!
try {
Oid mechOid = getOidFromMechFactoryProperty(prop);
mechs.add(mechOid);
retVal = true;
} catch (GSSException e) {
// Skip to next property
GSSUtil.debug("Ignore the invalid property " +
prop + " from provider " + p.getName());
}
} // Processed GSS property
} // while loop
return retVal;
}
/**
* Stores a provider and a mechanism oid indicating that the
* provider should be used for the mechanism. If the mechanism
* Oid is null, then it indicates that this preference holds for
* any mechanism.<p>
*
* The ProviderList maintains an ordered list of
* PreferencesEntry's and iterates thru them as it tries to
* instantiate MechanismFactory's.
*/
private static final class PreferencesEntry {
private Provider p;
private Oid oid;
PreferencesEntry(Provider p, Oid oid) {
this.p = p;
this.oid = oid;
}
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof PreferencesEntry)) {
return false;
}
PreferencesEntry that = (PreferencesEntry)other;
if (this.p.getName().equals(that.p.getName())) {
if (this.oid != null && that.oid != null) {
return this.oid.equals(that.oid);
} else {
return (this.oid == null && that.oid == null);
}
}
return false;
}
public int hashCode() {
int result = 17;
result = 37 * result + p.getName().hashCode();
if (oid != null) {
result = 37 * result + oid.hashCode();
}
return result;
}
/**
* Determines if a preference implies another. A preference
* implies another if the latter is subsumed by the
* former. e.g., <Provider1, null> implies <Provider1, OidX>
* because the null in the former indicates that it should
* be used for all mechanisms.
*/
boolean implies(Object other) {
if (other instanceof PreferencesEntry) {
PreferencesEntry temp = (PreferencesEntry) other;
return (equals(temp) ||
p.getName().equals(temp.p.getName()) &&
oid == null);
} else {
return false;
}
}
Provider getProvider() {
return p;
}
Oid getOid() {
return oid;
}
/**
* Determines if this entry is applicable to the desired
* mechanism. The entry is applicable to the desired mech if
* it contains the same oid or if it contains a null oid
* indicating that it is applicable to all mechs.
* @param mechOid the desired mechanism
* @return true if the provider in this entry should be
* queried for this mechanism.
*/
boolean impliesMechanism(Oid oid) {
return (this.oid == null || this.oid.equals(oid));
}
// For debugging
public String toString() {
StringBuilder sb = new StringBuilder("<");
sb.append(p.getName());
sb.append(", ");
sb.append(oid);
sb.append(">");
return sb.toString();
}
}
}

View file

@ -0,0 +1,118 @@
/*
* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import java.security.Provider;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidParameterException;
import java.security.ProviderException;
import sun.security.jgss.krb5.Krb5MechFactory;
import sun.security.jgss.spnego.SpNegoMechFactory;
import static sun.security.util.SecurityConstants.PROVIDER_VER;
/**
* Defines the Sun JGSS provider.
* Will merger this with the Sun security provider
* sun.security.provider.Sun when the JGSS src is merged with the JDK
* src.
*
* Mechanisms supported are:
*
* - Kerberos v5 as defined in RFC 1964.
* Oid is 1.2.840.113554.1.2.2
*
* - SPNEGO as defined in RFC 2478
* Oid is 1.3.6.1.5.5.2
*
* [Dummy mechanism is no longer compiled:
* - Dummy mechanism. This is primarily useful to test a multi-mech
* environment.
* Oid is 1.3.6.1.4.1.42.2.26.1.2]
*
* @author Mayank Upadhyay
*/
public final class SunProvider extends Provider {
private static final long serialVersionUID = -238911724858694198L;
private static final String INFO = "Sun " +
"(Kerberos v5, SPNEGO)";
// "(Kerberos v5, Dummy GSS-API Mechanism)";
private static final class ProviderService extends Provider.Service {
ProviderService(Provider p, String type, String algo, String cn) {
super(p, type, algo, cn, null, null);
}
@Override
public Object newInstance(Object ctrParamObj)
throws NoSuchAlgorithmException {
String type = getType();
if (ctrParamObj != null) {
throw new InvalidParameterException
("constructorParameter not used with " + type +
" engines");
}
String algo = getAlgorithm();
try {
if (type.equals("GssApiMechanism")) {
if (algo.equals("1.2.840.113554.1.2.2")) {
return new Krb5MechFactory();
} else if (algo.equals("1.3.6.1.5.5.2")) {
return new SpNegoMechFactory();
}
}
} catch (Exception ex) {
throw new NoSuchAlgorithmException
("Error constructing " + type + " for " +
algo + " using SunJGSS", ex);
}
throw new ProviderException("No impl for " + algo +
" " + type);
}
}
public SunProvider() {
/* We are the Sun JGSS provider */
super("SunJGSS", PROVIDER_VER, INFO);
final Provider p = this;
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
putService(new ProviderService(p, "GssApiMechanism",
"1.2.840.113554.1.2.2",
"sun.security.jgss.krb5.Krb5MechFactory"));
putService(new ProviderService(p, "GssApiMechanism",
"1.3.6.1.5.5.2",
"sun.security.jgss.spnego.SpNegoMechFactory"));
return null;
}
});
}
}

View file

@ -0,0 +1,419 @@
/*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss;
import org.ietf.jgss.MessageProp;
import java.util.LinkedList;
/**
* A utility class that implements a number list that keeps track of which
* tokens have arrived by storing their token numbers in the list. It helps
* detect old tokens, out of sequence tokens, and duplicate tokens.
*
* Each element of the list is an interval [a, b]. Its existence in the
* list implies that all token numbers in the range a, a+1, ..., b-1, b
* have arrived. Gaps in arrived token numbers are represented by the
* numbers that fall in between two elements of the list. eg. {[a,b],
* [c,d]} indicates that the token numbers b+1, ..., c-1 have not arrived
* yet.
*
* The maximum number of intervals that we keep track of is
* MAX_INTERVALS. Thus if there are too many gaps, then some of the older
* sequence numbers are deleted from the list. The earliest sequence number
* that exists in the list is the windowStart. The next expected sequence
* number, or expectedNumber, is one greater than the latest sequence
* number in the list.
*
* The list keeps track the first token number that should have arrived
* (initNumber) so that it is able to detect if certain numbers occur after
* the first valid token number but before windowStart. That would happen
* if the number of elements (intervals) exceeds MAX_INTERVALS and some
* initial elements had to be deleted.
*
* The working of the list is optimized for the normal case where the
* tokens arrive in sequence.
*
* @author Mayank Upadhyay
* @since 1.4
*/
public class TokenTracker {
static final int MAX_INTERVALS = 5;
private int initNumber;
private int windowStart;
private int expectedNumber;
private int windowStartIndex = 0;
private LinkedList<Entry> list = new LinkedList<Entry>();
public TokenTracker(int initNumber) {
this.initNumber = initNumber;
this.windowStart = initNumber;
this.expectedNumber = initNumber;
// Make an entry with one less than the expected first token
Entry entry = new Entry(initNumber-1);
list.add(entry);
}
/**
* Returns the index for the entry into which this number will fit. If
* there is none, it returns the index of the last interval
* which precedes this number. It returns -1 if the number needs to be
* a in a new interval ahead of the whole list.
*/
private int getIntervalIndex(int number) {
Entry entry = null;
int i;
// Start from the rear to optimize for the normal case
for (i = list.size() - 1; i >= 0; i--) {
entry = list.get(i);
if (entry.compareTo(number) <= 0)
break;
}
return i;
}
/**
* Sets the sequencing and replay information for the given token
* number.
*
* The following represents the number line with positions of
* initNumber, windowStart, expectedNumber marked on it. Regions in
* between them show the different sequencing and replay state
* possibilites for tokens that fall in there.
*
* (1) windowStart
* initNumber expectedNumber
* | |
* ---|---------------------------|---
* GAP | DUP/UNSEQ | GAP
*
*
* (2) initNumber windowStart expectedNumber
* | | |
* ---|---------------|--------------|---
* GAP | OLD | DUP/UNSEQ | GAP
*
*
* (3) windowStart
* expectedNumber initNumber
* | |
* ---|---------------------------|---
* DUP/UNSEQ | GAP | DUP/UNSEQ
*
*
* (4) expectedNumber initNumber windowStart
* | | |
* ---|---------------|--------------|---
* DUP/UNSEQ | GAP | OLD | DUP/UNSEQ
*
*
*
* (5) windowStart expectedNumber initNumber
* | | |
* ---|---------------|--------------|---
* OLD | DUP/UNSEQ | GAP | OLD
*
*
*
* (This analysis leaves out the possibility that expectedNumber passes
* initNumber after wrapping around. That may be added later.)
*/
synchronized public final void getProps(int number, MessageProp prop) {
boolean gap = false;
boolean old = false;
boolean unsequenced = false;
boolean duplicate = false;
// System.out.println("\n\n==========");
// System.out.println("TokenTracker.getProps(): number=" + number);
// System.out.println(toString());
int pos = getIntervalIndex(number);
Entry entry = null;
if (pos != -1)
entry = list.get(pos);
// Optimize for the expected case:
if (number == expectedNumber) {
expectedNumber++;
} else {
// Next trivial case is to check for duplicate
if (entry != null && entry.contains(number))
duplicate = true;
else {
if (expectedNumber >= initNumber) {
// Cases (1) and (2)
if (number > expectedNumber) {
gap = true;
} else if (number >= windowStart) {
unsequenced = true;
} else if (number >= initNumber) {
old = true;
} else {
gap = true;
}
} else {
// Cases (3), (4) and (5)
if (number > expectedNumber) {
if (number < initNumber) {
gap = true;
} else if (windowStart >= initNumber) {
if (number >= windowStart) {
unsequenced = true;
} else
old = true;
} else {
old = true;
}
} else if (windowStart > expectedNumber) {
unsequenced = true;
} else if (number < windowStart) {
old = true;
} else
unsequenced = true;
}
}
}
if (!duplicate && !old)
add(number, pos);
if (gap)
expectedNumber = number+1;
prop.setSupplementaryStates(duplicate, old, unsequenced, gap,
0, null);
// System.out.println("Leaving with state:");
// System.out.println(toString());
// System.out.println("==========\n");
}
/**
* Adds the number to the list just after the entry that is currently
* at position prevEntryPos. If prevEntryPos is -1, then the number
* will begin a new interval at the front of the list.
*/
private void add(int number, int prevEntryPos) {
Entry entry;
Entry entryBefore = null;
Entry entryAfter = null;
boolean appended = false;
boolean prepended = false;
if (prevEntryPos != -1) {
entryBefore = list.get(prevEntryPos);
// Can this number simply be added to the previous interval?
if (number == (entryBefore.getEnd() + 1)) {
entryBefore.setEnd(number);
appended = true;
}
}
// Now check the interval that follows this number
int nextEntryPos = prevEntryPos + 1;
if ((nextEntryPos) < list.size()) {
entryAfter = list.get(nextEntryPos);
// Can this number simply be added to the next interval?
if (number == (entryAfter.getStart() - 1)) {
if (!appended) {
entryAfter.setStart(number);
} else {
// Merge the two entries
entryAfter.setStart(entryBefore.getStart());
list.remove(prevEntryPos);
// Index of any entry following this gets decremented
if (windowStartIndex > prevEntryPos)
windowStartIndex--;
}
prepended = true;
}
}
if (prepended || appended)
return;
/*
* At this point we know that the number will start a new interval
* which needs to be added to the list. We might have to recyle an
* older entry in the list.
*/
if (list.size() < MAX_INTERVALS) {
entry = new Entry(number);
if (prevEntryPos < windowStartIndex)
windowStartIndex++; // due to the insertion which will happen
} else {
/*
* Delete the entry that marks the start of the current window.
* The marker will automatically point to the next entry in the
* list when this happens. If the current entry is at the end
* of the list then set the marker to the start of the list.
*/
int oldWindowStartIndex = windowStartIndex;
if (windowStartIndex == (list.size() - 1))
windowStartIndex = 0;
entry = list.remove(oldWindowStartIndex);
windowStart = list.get(windowStartIndex).getStart();
entry.setStart(number);
entry.setEnd(number);
if (prevEntryPos >= oldWindowStartIndex) {
prevEntryPos--; // due to the deletion that just happened
} else {
/*
* If the start of the current window just moved from the
* end of the list to the front of the list, and if the new
* entry will be added to the front of the list, then
* the new entry is the actual window start.
* eg, Consider { [-10, -8], ..., [-6, -3], [3, 9]}. In
* this list, suppose the element [3, 9] is the start of
* the window and has to be deleted to make place to add
* [-12, -12]. The resultant list will be
* {[-12, -12], [-10, -8], ..., [-6, -3]} and the new start
* of the window should be the element [-12, -12], not
* [-10, -8] which succeeded [3, 9] in the old list.
*/
if (oldWindowStartIndex != windowStartIndex) {
// windowStartIndex is 0 at this point
if (prevEntryPos == -1)
// The new entry is going to the front
windowStart = number;
} else {
// due to the insertion which will happen:
windowStartIndex++;
}
}
}
// Finally we are ready to actually add to the list at index
// 'prevEntryPos+1'
list.add(prevEntryPos+1, entry);
}
public String toString() {
StringBuilder sb = new StringBuilder("TokenTracker: ");
sb.append(" initNumber=").append(initNumber);
sb.append(" windowStart=").append(windowStart);
sb.append(" expectedNumber=").append(expectedNumber);
sb.append(" windowStartIndex=").append(windowStartIndex);
sb.append("\n\tIntervals are: {");
for (int i = 0; i < list.size(); i++) {
if (i != 0)
sb.append(", ");
sb.append(list.get(i).toString());
}
sb.append('}');
return sb.toString();
}
/**
* An entry in the list that represents the sequence of received
* tokens. Each entry is actaully an interval of numbers, all of which
* have been received.
*/
class Entry {
private int start;
private int end;
Entry(int number) {
start = number;
end = number;
}
/**
* Returns -1 if this interval represented by this entry precedes
* the number, 0 if the number is contained in the interval,
* and -1 if the interval occurs after the number.
*/
final int compareTo(int number) {
if (start > number)
return 1;
else if (end < number)
return -1;
else
return 0;
}
final boolean contains(int number) {
return (number >= start &&
number <= end);
}
final void append(int number) {
if (number == (end + 1))
end = number;
}
final void setInterval(int start, int end) {
this.start = start;
this.end = end;
}
final void setEnd(int end) {
this.end = end;
}
final void setStart(int start) {
this.start = start;
}
final int getStart() {
return start;
}
final int getEnd() {
return end;
}
public String toString() {
return ("[" + start + ", " + end + "]");
}
}
}

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import java.io.InputStream;
import java.io.IOException;
import java.security.AccessController;
import sun.security.action.GetBooleanAction;
import sun.security.krb5.*;
class AcceptSecContextToken extends InitialToken {
private KrbApRep apRep = null;
/**
* Creates an AcceptSecContextToken for the context acceptor to send to
* the context initiator.
*/
public AcceptSecContextToken(Krb5Context context,
KrbApReq apReq)
throws KrbException, IOException, GSSException {
boolean useSubkey = AccessController.doPrivileged(
new GetBooleanAction("sun.security.krb5.acceptor.subkey"));
boolean useSequenceNumber = true;
EncryptionKey subKey = null;
if (useSubkey) {
subKey = new EncryptionKey(apReq.getCreds().getSessionKey());
context.setKey(Krb5Context.ACCEPTOR_SUBKEY, subKey);
}
apRep = new KrbApRep(apReq, useSequenceNumber, subKey);
context.resetMySequenceNumber(apRep.getSeqNumber().intValue());
/*
* Note: The acceptor side context key was set when the
* InitSecContextToken was received.
*/
}
/**
* Creates an AcceptSecContextToken at the context initiator's side
* using the bytes received from the acceptor.
*/
public AcceptSecContextToken(Krb5Context context,
Credentials serviceCreds, KrbApReq apReq,
InputStream is)
throws IOException, GSSException, KrbException {
int tokenId = ((is.read()<<8) | is.read());
if (tokenId != Krb5Token.AP_REP_ID)
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"AP_REP token id does not match!");
byte[] apRepBytes =
new sun.security.util.DerValue(is).toByteArray();
KrbApRep apRep = new KrbApRep(apRepBytes, serviceCreds, apReq);
/*
* Allow the context acceptor to set a subkey if desired, even
* though our context acceptor will not do so.
*/
EncryptionKey subKey = apRep.getSubKey();
if (subKey != null) {
context.setKey(Krb5Context.ACCEPTOR_SUBKEY, subKey);
/*
System.out.println("\n\nSub-Session key from AP-REP is: " +
getHexBytes(subKey.getBytes()) + "\n");
*/
}
Integer apRepSeqNumber = apRep.getSeqNumber();
int peerSeqNumber = (apRepSeqNumber != null ?
apRepSeqNumber.intValue() :
0);
context.resetPeerSequenceNumber(peerSeqNumber);
}
public final byte[] encode() throws IOException {
byte[] apRepBytes = apRep.getMessage();
byte[] retVal = new byte[2 + apRepBytes.length];
writeInt(Krb5Token.AP_REP_ID, retVal, 0);
System.arraycopy(apRepBytes, 0, retVal, 2, apRepBytes.length);
return retVal;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,170 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import java.io.InputStream;
import java.io.IOException;
import sun.security.krb5.*;
import java.net.InetAddress;
import sun.security.krb5.internal.AuthorizationData;
import sun.security.krb5.internal.KerberosTime;
class InitSecContextToken extends InitialToken {
private KrbApReq apReq = null;
/**
* For the context initiator to call. It constructs a new
* InitSecContextToken to send over to the peer containing the desired
* flags and the AP-REQ. It also updates the context with the local
* sequence number and shared context key.
* (When mutual auth is enabled the peer has an opportunity to
* renegotiate the session key in the followup AcceptSecContextToken
* that it sends.)
*/
InitSecContextToken(Krb5Context context,
Credentials tgt,
Credentials serviceTicket)
throws KrbException, IOException, GSSException {
boolean mutualRequired = context.getMutualAuthState();
boolean useSubkey = true; // MIT Impl will crash if this is not set!
boolean useSequenceNumber = true;
OverloadedChecksum gssChecksum =
new OverloadedChecksum(context, tgt, serviceTicket);
Checksum checksum = gssChecksum.getChecksum();
context.setTktFlags(serviceTicket.getFlags());
context.setAuthTime(
new KerberosTime(serviceTicket.getAuthTime()).toString());
apReq = new KrbApReq(serviceTicket,
mutualRequired,
useSubkey,
useSequenceNumber,
checksum);
context.resetMySequenceNumber(apReq.getSeqNumber().intValue());
EncryptionKey subKey = apReq.getSubKey();
if (subKey != null)
context.setKey(Krb5Context.INITIATOR_SUBKEY, subKey);
else
context.setKey(Krb5Context.SESSION_KEY, serviceTicket.getSessionKey());
if (!mutualRequired)
context.resetPeerSequenceNumber(0);
}
/**
* For the context acceptor to call. It reads the bytes out of an
* InputStream and constructs an InitSecContextToken with them.
*/
InitSecContextToken(Krb5Context context, Krb5AcceptCredential cred,
InputStream is)
throws IOException, GSSException, KrbException {
int tokenId = ((is.read()<<8) | is.read());
if (tokenId != Krb5Token.AP_REQ_ID)
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"AP_REQ token id does not match!");
// XXX Modify KrbApReq cons to take an InputStream
byte[] apReqBytes =
new sun.security.util.DerValue(is).toByteArray();
//debug("=====ApReqBytes: [" + getHexBytes(apReqBytes) + "]\n");
InetAddress addr = null;
if (context.getChannelBinding() != null) {
addr = context.getChannelBinding().getInitiatorAddress();
}
apReq = new KrbApReq(apReqBytes, cred, addr);
//debug("\nReceived AP-REQ and authenticated it.\n");
EncryptionKey sessionKey = apReq.getCreds().getSessionKey();
/*
System.out.println("\n\nSession key from service ticket is: " +
getHexBytes(sessionKey.getBytes()));
*/
EncryptionKey subKey = apReq.getSubKey();
if (subKey != null) {
context.setKey(Krb5Context.INITIATOR_SUBKEY, subKey);
/*
System.out.println("Sub-Session key from authenticator is: " +
getHexBytes(subKey.getBytes()) + "\n");
*/
} else {
context.setKey(Krb5Context.SESSION_KEY, sessionKey);
//System.out.println("Sub-Session Key Missing in Authenticator.\n");
}
OverloadedChecksum gssChecksum = new OverloadedChecksum(
context, apReq.getChecksum(), sessionKey, subKey);
gssChecksum.setContextFlags(context);
Credentials delegCred = gssChecksum.getDelegatedCreds();
if (delegCred != null) {
Krb5CredElement credElement =
Krb5InitCredential.getInstance(
(Krb5NameElement)context.getSrcName(),
delegCred);
context.setDelegCred(credElement);
}
Integer apReqSeqNumber = apReq.getSeqNumber();
int peerSeqNumber = (apReqSeqNumber != null ?
apReqSeqNumber.intValue() :
0);
context.resetPeerSequenceNumber(peerSeqNumber);
if (!context.getMutualAuthState())
// Use the same sequence number as the peer
// (Behaviour exhibited by the Windows SSPI server)
context.resetMySequenceNumber(peerSeqNumber);
context.setAuthTime(
new KerberosTime(apReq.getCreds().getAuthTime()).toString());
context.setTktFlags(apReq.getCreds().getFlags());
AuthorizationData ad = apReq.getCreds().getAuthzData();
context.setAuthzData(ad);
}
public final KrbApReq getKrbApReq() {
return apReq;
}
public final byte[] encode() throws IOException {
byte[] apReqBytes = apReq.getMessage();
byte[] retVal = new byte[2 + apReqBytes.length];
writeInt(Krb5Token.AP_REQ_ID, retVal, 0);
System.arraycopy(apReqBytes, 0, retVal, 2, apReqBytes.length);
// System.out.println("GSS-Token with AP_REQ is:");
// System.out.println(getHexBytes(retVal));
return retVal;
}
}

View file

@ -0,0 +1,453 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import javax.security.auth.kerberos.DelegationPermission;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import sun.security.krb5.*;
import sun.security.krb5.internal.Krb5;
abstract class InitialToken extends Krb5Token {
private static final int CHECKSUM_TYPE = 0x8003;
private static final int CHECKSUM_LENGTH_SIZE = 4;
private static final int CHECKSUM_BINDINGS_SIZE = 16;
private static final int CHECKSUM_FLAGS_SIZE = 4;
private static final int CHECKSUM_DELEG_OPT_SIZE = 2;
private static final int CHECKSUM_DELEG_LGTH_SIZE = 2;
private static final int CHECKSUM_DELEG_FLAG = 1;
private static final int CHECKSUM_MUTUAL_FLAG = 2;
private static final int CHECKSUM_REPLAY_FLAG = 4;
private static final int CHECKSUM_SEQUENCE_FLAG = 8;
private static final int CHECKSUM_CONF_FLAG = 16;
private static final int CHECKSUM_INTEG_FLAG = 32;
private final byte[] CHECKSUM_FIRST_BYTES =
{(byte)0x10, (byte)0x00, (byte)0x00, (byte)0x00};
private static final int CHANNEL_BINDING_AF_INET = 2;
private static final int CHANNEL_BINDING_AF_INET6 = 24;
private static final int CHANNEL_BINDING_AF_NULL_ADDR = 255;
private static final int Inet4_ADDRSZ = 4;
private static final int Inet6_ADDRSZ = 16;
protected class OverloadedChecksum {
private byte[] checksumBytes = null;
private Credentials delegCreds = null;
private int flags = 0;
/**
* Called on the initiator side when creating the
* InitSecContextToken.
*/
public OverloadedChecksum(Krb5Context context,
Credentials tgt,
Credentials serviceTicket)
throws KrbException, IOException, GSSException {
byte[] krbCredMessage = null;
int pos = 0;
int size = CHECKSUM_LENGTH_SIZE + CHECKSUM_BINDINGS_SIZE +
CHECKSUM_FLAGS_SIZE;
if (!tgt.isForwardable()) {
context.setCredDelegState(false);
context.setDelegPolicyState(false);
} else if (context.getCredDelegState()) {
if (context.getDelegPolicyState()) {
if (!serviceTicket.checkDelegate()) {
// delegation not permitted by server policy, mark it
context.setDelegPolicyState(false);
}
}
} else if (context.getDelegPolicyState()) {
if (serviceTicket.checkDelegate()) {
context.setCredDelegState(true);
} else {
context.setDelegPolicyState(false);
}
}
if (context.getCredDelegState()) {
KrbCred krbCred = null;
CipherHelper cipherHelper =
context.getCipherHelper(serviceTicket.getSessionKey());
if (useNullKey(cipherHelper)) {
krbCred = new KrbCred(tgt, serviceTicket,
EncryptionKey.NULL_KEY);
} else {
krbCred = new KrbCred(tgt, serviceTicket,
serviceTicket.getSessionKey());
}
krbCredMessage = krbCred.getMessage();
size += CHECKSUM_DELEG_OPT_SIZE +
CHECKSUM_DELEG_LGTH_SIZE +
krbCredMessage.length;
}
checksumBytes = new byte[size];
checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[0];
checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[1];
checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[2];
checksumBytes[pos++] = CHECKSUM_FIRST_BYTES[3];
ChannelBinding localBindings = context.getChannelBinding();
if (localBindings != null) {
byte[] localBindingsBytes =
computeChannelBinding(context.getChannelBinding());
System.arraycopy(localBindingsBytes, 0,
checksumBytes, pos, localBindingsBytes.length);
// System.out.println("ChannelBinding hash: "
// + getHexBytes(localBindingsBytes));
}
pos += CHECKSUM_BINDINGS_SIZE;
if (context.getCredDelegState())
flags |= CHECKSUM_DELEG_FLAG;
if (context.getMutualAuthState())
flags |= CHECKSUM_MUTUAL_FLAG;
if (context.getReplayDetState())
flags |= CHECKSUM_REPLAY_FLAG;
if (context.getSequenceDetState())
flags |= CHECKSUM_SEQUENCE_FLAG;
if (context.getIntegState())
flags |= CHECKSUM_INTEG_FLAG;
if (context.getConfState())
flags |= CHECKSUM_CONF_FLAG;
byte[] temp = new byte[4];
writeLittleEndian(flags, temp);
checksumBytes[pos++] = temp[0];
checksumBytes[pos++] = temp[1];
checksumBytes[pos++] = temp[2];
checksumBytes[pos++] = temp[3];
if (context.getCredDelegState()) {
PrincipalName delegateTo =
serviceTicket.getServer();
// Cannot use '\"' instead of "\"" in constructor because
// it is interpreted as suggested length!
StringBuilder sb = new StringBuilder("\"");
sb.append(delegateTo.getName()).append('\"');
String realm = delegateTo.getRealmAsString();
sb.append(" \"krbtgt/").append(realm).append('@');
sb.append(realm).append('\"');
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
DelegationPermission perm =
new DelegationPermission(sb.toString());
sm.checkPermission(perm);
}
/*
* Write 1 in little endian but in two bytes
* for DlgOpt
*/
checksumBytes[pos++] = (byte)0x01;
checksumBytes[pos++] = (byte)0x00;
/*
* Write the length of the delegated credential in little
* endian but in two bytes for Dlgth
*/
if (krbCredMessage.length > 0x0000ffff)
throw new GSSException(GSSException.FAILURE, -1,
"Incorrect message length");
writeLittleEndian(krbCredMessage.length, temp);
checksumBytes[pos++] = temp[0];
checksumBytes[pos++] = temp[1];
System.arraycopy(krbCredMessage, 0,
checksumBytes, pos, krbCredMessage.length);
}
}
/**
* Called on the acceptor side when reading an InitSecContextToken.
*/
// XXX Passing in Checksum is not required. byte[] can
// be passed in if this checksum type denotes a
// raw_checksum. In that case, make Checksum class krb5
// internal.
public OverloadedChecksum(Krb5Context context, Checksum checksum,
EncryptionKey key, EncryptionKey subKey)
throws GSSException, KrbException, IOException {
int pos = 0;
if (checksum == null) {
GSSException ge = new GSSException(GSSException.FAILURE, -1,
"No cksum in AP_REQ's authenticator");
ge.initCause(new KrbException(Krb5.KRB_AP_ERR_INAPP_CKSUM));
throw ge;
}
checksumBytes = checksum.getBytes();
if ((checksumBytes[0] != CHECKSUM_FIRST_BYTES[0]) ||
(checksumBytes[1] != CHECKSUM_FIRST_BYTES[1]) ||
(checksumBytes[2] != CHECKSUM_FIRST_BYTES[2]) ||
(checksumBytes[3] != CHECKSUM_FIRST_BYTES[3])) {
throw new GSSException(GSSException.FAILURE, -1,
"Incorrect checksum");
}
ChannelBinding localBindings = context.getChannelBinding();
// Ignore remote channel binding info when not requested at
// local side (RFC 4121 4.1.1.2: the acceptor MAY ignore...).
//
// All major krb5 implementors implement this "MAY",
// and some applications depend on it as a workaround
// for not having a way to negotiate the use of channel
// binding -- the initiator application always uses CB
// and hopes the acceptor will ignore the CB if the
// acceptor doesn't support CB.
if (localBindings != null) {
byte[] remoteBindingBytes = new byte[CHECKSUM_BINDINGS_SIZE];
System.arraycopy(checksumBytes, 4, remoteBindingBytes, 0,
CHECKSUM_BINDINGS_SIZE);
byte[] noBindings = new byte[CHECKSUM_BINDINGS_SIZE];
if (!Arrays.equals(noBindings, remoteBindingBytes)) {
byte[] localBindingsBytes =
computeChannelBinding(localBindings);
if (!Arrays.equals(localBindingsBytes,
remoteBindingBytes)) {
throw new GSSException(GSSException.BAD_BINDINGS, -1,
"Bytes mismatch!");
}
} else {
throw new GSSException(GSSException.BAD_BINDINGS, -1,
"Token missing ChannelBinding!");
}
}
flags = readLittleEndian(checksumBytes, 20, 4);
if ((flags & CHECKSUM_DELEG_FLAG) > 0) {
/*
* XXX
* if ((checksumBytes[24] != (byte)0x01) &&
* (checksumBytes[25] != (byte)0x00))
*/
int credLen = readLittleEndian(checksumBytes, 26, 2);
byte[] credBytes = new byte[credLen];
System.arraycopy(checksumBytes, 28, credBytes, 0, credLen);
KrbCred cred;
try {
cred = new KrbCred(credBytes, key);
} catch (KrbException ke) {
if (subKey != null) {
cred = new KrbCred(credBytes, subKey);
} else {
throw ke;
}
}
delegCreds = cred.getDelegatedCreds()[0];
}
}
// check if KRB-CRED message should use NULL_KEY for encryption
private boolean useNullKey(CipherHelper ch) {
boolean flag = true;
// for "newer" etypes and RC4-HMAC do not use NULL KEY
if ((ch.getProto() == 1) || ch.isArcFour()) {
flag = false;
}
return flag;
}
public Checksum getChecksum() throws KrbException {
return new Checksum(checksumBytes, CHECKSUM_TYPE);
}
public Credentials getDelegatedCreds() {
return delegCreds;
}
// Only called by acceptor
public void setContextFlags(Krb5Context context) {
// default for cred delegation is false
if ((flags & CHECKSUM_DELEG_FLAG) > 0)
context.setCredDelegState(true);
// default for the following are true
if ((flags & CHECKSUM_MUTUAL_FLAG) == 0) {
context.setMutualAuthState(false);
}
if ((flags & CHECKSUM_REPLAY_FLAG) == 0) {
context.setReplayDetState(false);
}
if ((flags & CHECKSUM_SEQUENCE_FLAG) == 0) {
context.setSequenceDetState(false);
}
if ((flags & CHECKSUM_CONF_FLAG) == 0) {
context.setConfState(false);
}
if ((flags & CHECKSUM_INTEG_FLAG) == 0) {
context.setIntegState(false);
}
}
}
private int getAddrType(InetAddress addr) {
int addressType = CHANNEL_BINDING_AF_NULL_ADDR;
if (addr instanceof Inet4Address)
addressType = CHANNEL_BINDING_AF_INET;
else if (addr instanceof Inet6Address)
addressType = CHANNEL_BINDING_AF_INET6;
return (addressType);
}
private byte[] getAddrBytes(InetAddress addr) throws GSSException {
int addressType = getAddrType(addr);
byte[] addressBytes = addr.getAddress();
if (addressBytes != null) {
switch (addressType) {
case CHANNEL_BINDING_AF_INET:
if (addressBytes.length != Inet4_ADDRSZ) {
throw new GSSException(GSSException.FAILURE, -1,
"Incorrect AF-INET address length in ChannelBinding.");
}
return (addressBytes);
case CHANNEL_BINDING_AF_INET6:
if (addressBytes.length != Inet6_ADDRSZ) {
throw new GSSException(GSSException.FAILURE, -1,
"Incorrect AF-INET6 address length in ChannelBinding.");
}
return (addressBytes);
default:
throw new GSSException(GSSException.FAILURE, -1,
"Cannot handle non AF-INET addresses in ChannelBinding.");
}
}
return null;
}
private byte[] computeChannelBinding(ChannelBinding channelBinding)
throws GSSException {
InetAddress initiatorAddress = channelBinding.getInitiatorAddress();
InetAddress acceptorAddress = channelBinding.getAcceptorAddress();
int size = 5*4;
int initiatorAddressType = getAddrType(initiatorAddress);
int acceptorAddressType = getAddrType(acceptorAddress);
byte[] initiatorAddressBytes = null;
if (initiatorAddress != null) {
initiatorAddressBytes = getAddrBytes(initiatorAddress);
size += initiatorAddressBytes.length;
}
byte[] acceptorAddressBytes = null;
if (acceptorAddress != null) {
acceptorAddressBytes = getAddrBytes(acceptorAddress);
size += acceptorAddressBytes.length;
}
byte[] appDataBytes = channelBinding.getApplicationData();
if (appDataBytes != null) {
size += appDataBytes.length;
}
byte[] data = new byte[size];
int pos = 0;
writeLittleEndian(initiatorAddressType, data, pos);
pos += 4;
if (initiatorAddressBytes != null) {
writeLittleEndian(initiatorAddressBytes.length, data, pos);
pos += 4;
System.arraycopy(initiatorAddressBytes, 0,
data, pos, initiatorAddressBytes.length);
pos += initiatorAddressBytes.length;
} else {
// Write length 0
pos += 4;
}
writeLittleEndian(acceptorAddressType, data, pos);
pos += 4;
if (acceptorAddressBytes != null) {
writeLittleEndian(acceptorAddressBytes.length, data, pos);
pos += 4;
System.arraycopy(acceptorAddressBytes, 0,
data, pos, acceptorAddressBytes.length);
pos += acceptorAddressBytes.length;
} else {
// Write length 0
pos += 4;
}
if (appDataBytes != null) {
writeLittleEndian(appDataBytes.length, data, pos);
pos += 4;
System.arraycopy(appDataBytes, 0, data, pos,
appDataBytes.length);
pos += appDataBytes.length;
} else {
// Write 0
pos += 4;
}
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
return md5.digest(data);
} catch (NoSuchAlgorithmException e) {
throw new GSSException(GSSException.FAILURE, -1,
"Could not get MD5 Message Digest - "
+ e.getMessage());
}
}
public abstract byte[] encode() throws IOException;
}

View file

@ -0,0 +1,197 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import java.io.IOException;
import org.ietf.jgss.*;
import sun.security.jgss.GSSCaller;
import sun.security.jgss.spi.*;
import sun.security.krb5.*;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.AccessController;
import java.security.AccessControlContext;
import javax.security.auth.DestroyFailedException;
/**
* Implements the krb5 acceptor credential element.
*
* @author Mayank Upadhyay
* @since 1.4
*/
public class Krb5AcceptCredential
implements Krb5CredElement {
private final Krb5NameElement name;
private final ServiceCreds screds;
private Krb5AcceptCredential(Krb5NameElement name, ServiceCreds creds) {
/*
* Initialize this instance with the data from the acquired
* KerberosKey. This class needs to be a KerberosKey too
* hence we can't just store a reference.
*/
this.name = name;
this.screds = creds;
}
static Krb5AcceptCredential getInstance(final GSSCaller caller, Krb5NameElement name)
throws GSSException {
final String serverPrinc = (name == null? null:
name.getKrb5PrincipalName().getName());
final AccessControlContext acc = AccessController.getContext();
ServiceCreds creds = null;
try {
creds = AccessController.doPrivileged(
new PrivilegedExceptionAction<ServiceCreds>() {
public ServiceCreds run() throws Exception {
return Krb5Util.getServiceCreds(
caller == GSSCaller.CALLER_UNKNOWN ? GSSCaller.CALLER_ACCEPT: caller,
serverPrinc, acc);
}});
} catch (PrivilegedActionException e) {
GSSException ge =
new GSSException(GSSException.NO_CRED, -1,
"Attempt to obtain new ACCEPT credentials failed!");
ge.initCause(e.getException());
throw ge;
}
if (creds == null)
throw new GSSException(GSSException.NO_CRED, -1,
"Failed to find any Kerberos credentials");
if (name == null) {
String fullName = creds.getName();
if (fullName != null) {
name = Krb5NameElement.getInstance(fullName,
Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
}
}
return new Krb5AcceptCredential(name, creds);
}
/**
* Returns the principal name for this credential. The name
* is in mechanism specific format.
*
* @return GSSNameSpi representing principal name of this credential
* @exception GSSException may be thrown
*/
public final GSSNameSpi getName() throws GSSException {
return name;
}
/**
* Returns the init lifetime remaining.
*
* @return the init lifetime remaining in seconds
* @exception GSSException may be thrown
*/
public int getInitLifetime() throws GSSException {
return 0;
}
/**
* Returns the accept lifetime remaining.
*
* @return the accept lifetime remaining in seconds
* @exception GSSException may be thrown
*/
public int getAcceptLifetime() throws GSSException {
return GSSCredential.INDEFINITE_LIFETIME;
}
public boolean isInitiatorCredential() throws GSSException {
return false;
}
public boolean isAcceptorCredential() throws GSSException {
return true;
}
/**
* Returns the oid representing the underlying credential
* mechanism oid.
*
* @return the Oid for this credential mechanism
* @exception GSSException may be thrown
*/
public final Oid getMechanism() {
return Krb5MechFactory.GSS_KRB5_MECH_OID;
}
public final java.security.Provider getProvider() {
return Krb5MechFactory.PROVIDER;
}
public EncryptionKey[] getKrb5EncryptionKeys(PrincipalName princ) {
return screds.getEKeys(princ);
}
/**
* Called to invalidate this credential element.
*/
public void dispose() throws GSSException {
try {
destroy();
} catch (DestroyFailedException e) {
GSSException gssException =
new GSSException(GSSException.FAILURE, -1,
"Could not destroy credentials - " + e.getMessage());
gssException.initCause(e);
}
}
/**
* Destroys the locally cached EncryptionKey value and then calls
* destroy in the base class.
*/
public void destroy() throws DestroyFailedException {
screds.destroy();
}
/**
* Impersonation is only available on the initiator side. The
* service must starts as an initiator to get an initial TGT to complete
* the S4U2self protocol.
*/
@Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
Credentials cred = screds.getInitCred();
if (cred != null) {
return Krb5InitCredential.getInstance(this.name, cred)
.impersonate(name);
} else {
throw new GSSException(GSSException.FAILURE, -1,
"Only an initiate credentials can impersonate");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2000, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import sun.security.krb5.*;
import java.security.Provider;
/**
* Provides type safety for Krb5 credential elements.
*
* @author Mayank Upadhyay
* @since 1.4
*/
interface Krb5CredElement
extends GSSCredentialSpi {
}

View file

@ -0,0 +1,367 @@
/*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.jgss.GSSCaller;
import sun.security.jgss.spi.*;
import sun.security.krb5.*;
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KerberosPrincipal;
import java.net.InetAddress;
import java.io.IOException;
import java.util.Date;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
/**
* Implements the krb5 initiator credential element.
*
* @author Mayank Upadhyay
* @author Ram Marti
* @since 1.4
*/
public class Krb5InitCredential
extends KerberosTicket
implements Krb5CredElement {
private static final long serialVersionUID = 7723415700837898232L;
private Krb5NameElement name;
private Credentials krb5Credentials;
private Krb5InitCredential(Krb5NameElement name,
byte[] asn1Encoding,
KerberosPrincipal client,
KerberosPrincipal server,
byte[] sessionKey,
int keyType,
boolean[] flags,
Date authTime,
Date startTime,
Date endTime,
Date renewTill,
InetAddress[] clientAddresses)
throws GSSException {
super(asn1Encoding,
client,
server,
sessionKey,
keyType,
flags,
authTime,
startTime,
endTime,
renewTill,
clientAddresses);
this.name = name;
try {
// Cache this for later use by the sun.security.krb5 package.
krb5Credentials = new Credentials(asn1Encoding,
client.getName(),
server.getName(),
sessionKey,
keyType,
flags,
authTime,
startTime,
endTime,
renewTill,
clientAddresses);
} catch (KrbException e) {
throw new GSSException(GSSException.NO_CRED, -1,
e.getMessage());
} catch (IOException e) {
throw new GSSException(GSSException.NO_CRED, -1,
e.getMessage());
}
}
private Krb5InitCredential(Krb5NameElement name,
Credentials delegatedCred,
byte[] asn1Encoding,
KerberosPrincipal client,
KerberosPrincipal server,
byte[] sessionKey,
int keyType,
boolean[] flags,
Date authTime,
Date startTime,
Date endTime,
Date renewTill,
InetAddress[] clientAddresses)
throws GSSException {
super(asn1Encoding,
client,
server,
sessionKey,
keyType,
flags,
authTime,
startTime,
endTime,
renewTill,
clientAddresses);
this.name = name;
// A delegated cred does not have all fields set. So do not try to
// creat new Credentials out of the delegatedCred.
this.krb5Credentials = delegatedCred;
}
static Krb5InitCredential getInstance(GSSCaller caller, Krb5NameElement name,
int initLifetime)
throws GSSException {
KerberosTicket tgt = getTgt(caller, name, initLifetime);
if (tgt == null)
throw new GSSException(GSSException.NO_CRED, -1,
"Failed to find any Kerberos tgt");
if (name == null) {
String fullName = tgt.getClient().getName();
name = Krb5NameElement.getInstance(fullName,
Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
}
return new Krb5InitCredential(name,
tgt.getEncoded(),
tgt.getClient(),
tgt.getServer(),
tgt.getSessionKey().getEncoded(),
tgt.getSessionKeyType(),
tgt.getFlags(),
tgt.getAuthTime(),
tgt.getStartTime(),
tgt.getEndTime(),
tgt.getRenewTill(),
tgt.getClientAddresses());
}
static Krb5InitCredential getInstance(Krb5NameElement name,
Credentials delegatedCred)
throws GSSException {
EncryptionKey sessionKey = delegatedCred.getSessionKey();
/*
* all of the following data is optional in a KRB-CRED
* messages. This check for each field.
*/
PrincipalName cPrinc = delegatedCred.getClient();
PrincipalName sPrinc = delegatedCred.getServer();
KerberosPrincipal client = null;
KerberosPrincipal server = null;
Krb5NameElement credName = null;
if (cPrinc != null) {
String fullName = cPrinc.getName();
credName = Krb5NameElement.getInstance(fullName,
Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
client = new KerberosPrincipal(fullName);
}
// XXX Compare name to credName
if (sPrinc != null) {
server =
new KerberosPrincipal(sPrinc.getName(),
KerberosPrincipal.KRB_NT_SRV_INST);
}
return new Krb5InitCredential(credName,
delegatedCred,
delegatedCred.getEncoded(),
client,
server,
sessionKey.getBytes(),
sessionKey.getEType(),
delegatedCred.getFlags(),
delegatedCred.getAuthTime(),
delegatedCred.getStartTime(),
delegatedCred.getEndTime(),
delegatedCred.getRenewTill(),
delegatedCred.getClientAddresses());
}
/**
* Returns the principal name for this credential. The name
* is in mechanism specific format.
*
* @return GSSNameSpi representing principal name of this credential
* @exception GSSException may be thrown
*/
public final GSSNameSpi getName() throws GSSException {
return name;
}
/**
* Returns the init lifetime remaining.
*
* @return the init lifetime remaining in seconds
* @exception GSSException may be thrown
*/
public int getInitLifetime() throws GSSException {
int retVal = 0;
Date d = getEndTime();
if (d == null) {
return 0;
}
retVal = (int)(d.getTime() - (new Date().getTime()));
return retVal/1000;
}
/**
* Returns the accept lifetime remaining.
*
* @return the accept lifetime remaining in seconds
* @exception GSSException may be thrown
*/
public int getAcceptLifetime() throws GSSException {
return 0;
}
public boolean isInitiatorCredential() throws GSSException {
return true;
}
public boolean isAcceptorCredential() throws GSSException {
return false;
}
/**
* Returns the oid representing the underlying credential
* mechanism oid.
*
* @return the Oid for this credential mechanism
* @exception GSSException may be thrown
*/
public final Oid getMechanism() {
return Krb5MechFactory.GSS_KRB5_MECH_OID;
}
public final java.security.Provider getProvider() {
return Krb5MechFactory.PROVIDER;
}
/**
* Returns a sun.security.krb5.Credentials instance so that it maybe
* used in that package for th Kerberos protocol.
*/
Credentials getKrb5Credentials() {
return krb5Credentials;
}
/*
* XXX Call to this.refresh() should refresh the locally cached copy
* of krb5Credentials also.
*/
/**
* Called to invalidate this credential element.
*/
public void dispose() throws GSSException {
try {
destroy();
} catch (javax.security.auth.DestroyFailedException e) {
GSSException gssException =
new GSSException(GSSException.FAILURE, -1,
"Could not destroy credentials - " + e.getMessage());
gssException.initCause(e);
}
}
// XXX call to this.destroy() should destroy the locally cached copy
// of krb5Credentials and then call super.destroy().
private static KerberosTicket getTgt(GSSCaller caller, Krb5NameElement name,
int initLifetime)
throws GSSException {
final String clientPrincipal;
/*
* Find the TGT for the realm that the client is in. If the client
* name is not available, then use the default realm.
*/
if (name != null) {
clientPrincipal = (name.getKrb5PrincipalName()).getName();
} else {
clientPrincipal = null;
}
final AccessControlContext acc = AccessController.getContext();
try {
final GSSCaller realCaller = (caller == GSSCaller.CALLER_UNKNOWN)
? GSSCaller.CALLER_INITIATE
: caller;
return AccessController.doPrivileged(
new PrivilegedExceptionAction<KerberosTicket>() {
public KerberosTicket run() throws Exception {
// It's OK to use null as serverPrincipal. TGT is almost
// the first ticket for a principal and we use list.
return Krb5Util.getTicket(
realCaller,
clientPrincipal, null, acc);
}});
} catch (PrivilegedActionException e) {
GSSException ge =
new GSSException(GSSException.NO_CRED, -1,
"Attempt to obtain new INITIATE credentials failed!" +
" (" + e.getMessage() + ")");
ge.initCause(e.getException());
throw ge;
}
}
@Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
try {
Krb5NameElement kname = (Krb5NameElement)name;
Credentials newCred = Credentials.acquireS4U2selfCreds(
kname.getKrb5PrincipalName(), krb5Credentials);
return new Krb5ProxyCredential(this, kname, newCred.getTicket());
} catch (IOException | KrbException ke) {
GSSException ge =
new GSSException(GSSException.FAILURE, -1,
"Attempt to obtain S4U2self credentials failed!");
ge.initCause(ke);
throw ge;
}
}
}

View file

@ -0,0 +1,236 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.GSSCaller;
import sun.security.jgss.spi.*;
import javax.security.auth.kerberos.ServicePermission;
import java.security.Provider;
import java.util.Vector;
/**
* Krb5 Mechanism plug in for JGSS
* This is the properties object required by the JGSS framework.
* All mechanism specific information is defined here.
*
* @author Mayank Upadhyay
*/
public final class Krb5MechFactory implements MechanismFactory {
private static final boolean DEBUG = Krb5Util.DEBUG;
static final Provider PROVIDER =
new sun.security.jgss.SunProvider();
static final Oid GSS_KRB5_MECH_OID =
createOid("1.2.840.113554.1.2.2");
static final Oid NT_GSS_KRB5_PRINCIPAL =
createOid("1.2.840.113554.1.2.2.1");
private static Oid[] nameTypes =
new Oid[] { GSSName.NT_USER_NAME,
GSSName.NT_HOSTBASED_SERVICE,
GSSName.NT_EXPORT_NAME,
NT_GSS_KRB5_PRINCIPAL};
final private GSSCaller caller;
private static Krb5CredElement getCredFromSubject(GSSNameSpi name,
boolean initiate)
throws GSSException {
Vector<Krb5CredElement> creds =
GSSUtil.searchSubject(name, GSS_KRB5_MECH_OID, initiate,
(initiate ?
Krb5InitCredential.class :
Krb5AcceptCredential.class));
Krb5CredElement result = ((creds == null || creds.isEmpty()) ?
null : creds.firstElement());
// Force permission check before returning the cred to caller
if (result != null) {
if (initiate) {
checkInitCredPermission((Krb5NameElement) result.getName());
} else {
checkAcceptCredPermission
((Krb5NameElement) result.getName(), name);
}
}
return result;
}
public Krb5MechFactory() {
this(GSSCaller.CALLER_UNKNOWN);
}
public Krb5MechFactory(GSSCaller caller) {
this.caller = caller;
}
public GSSNameSpi getNameElement(String nameStr, Oid nameType)
throws GSSException {
return Krb5NameElement.getInstance(nameStr, nameType);
}
public GSSNameSpi getNameElement(byte[] name, Oid nameType)
throws GSSException {
// At this point, even an exported name is stripped down to safe
// bytes only
// XXX Use encoding here
return Krb5NameElement.getInstance(new String(name), nameType);
}
public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
int initLifetime, int acceptLifetime,
int usage) throws GSSException {
if (name != null && !(name instanceof Krb5NameElement)) {
name = Krb5NameElement.getInstance(name.toString(),
name.getStringNameType());
}
Krb5CredElement credElement = getCredFromSubject
(name, (usage != GSSCredential.ACCEPT_ONLY));
if (credElement == null) {
if (usage == GSSCredential.INITIATE_ONLY ||
usage == GSSCredential.INITIATE_AND_ACCEPT) {
credElement = Krb5InitCredential.getInstance
(caller, (Krb5NameElement) name, initLifetime);
checkInitCredPermission
((Krb5NameElement) credElement.getName());
} else if (usage == GSSCredential.ACCEPT_ONLY) {
credElement =
Krb5AcceptCredential.getInstance(caller,
(Krb5NameElement) name);
checkAcceptCredPermission
((Krb5NameElement) credElement.getName(), name);
} else
throw new GSSException(GSSException.FAILURE, -1,
"Unknown usage mode requested");
}
return credElement;
}
public static void checkInitCredPermission(Krb5NameElement name) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
String realm = (name.getKrb5PrincipalName()).getRealmAsString();
String tgsPrincipal =
new String("krbtgt/" + realm + '@' + realm);
ServicePermission perm =
new ServicePermission(tgsPrincipal, "initiate");
try {
sm.checkPermission(perm);
} catch (SecurityException e) {
if (DEBUG) {
System.out.println("Permission to initiate" +
"kerberos init credential" + e.getMessage());
}
throw e;
}
}
}
public static void checkAcceptCredPermission(Krb5NameElement name,
GSSNameSpi originalName) {
SecurityManager sm = System.getSecurityManager();
if (sm != null && name != null) {
ServicePermission perm = new ServicePermission
(name.getKrb5PrincipalName().getName(), "accept");
try {
sm.checkPermission(perm);
} catch (SecurityException e) {
if (originalName == null) {
// Don't disclose the name of the principal
e = new SecurityException("No permission to acquire "
+ "Kerberos accept credential");
// Don't call e.initCause() with caught exception
}
throw e;
}
}
}
public GSSContextSpi getMechanismContext(GSSNameSpi peer,
GSSCredentialSpi myInitiatorCred, int lifetime)
throws GSSException {
if (peer != null && !(peer instanceof Krb5NameElement)) {
peer = Krb5NameElement.getInstance(peer.toString(),
peer.getStringNameType());
}
// XXX Convert myInitiatorCred to Krb5CredElement
if (myInitiatorCred == null) {
myInitiatorCred = getCredentialElement(null, lifetime, 0,
GSSCredential.INITIATE_ONLY);
}
return new Krb5Context(caller, (Krb5NameElement)peer,
(Krb5CredElement)myInitiatorCred, lifetime);
}
public GSSContextSpi getMechanismContext(GSSCredentialSpi myAcceptorCred)
throws GSSException {
// XXX Convert myAcceptorCred to Krb5CredElement
if (myAcceptorCred == null) {
myAcceptorCred = getCredentialElement(null, 0,
GSSCredential.INDEFINITE_LIFETIME, GSSCredential.ACCEPT_ONLY);
}
return new Krb5Context(caller, (Krb5CredElement)myAcceptorCred);
}
public GSSContextSpi getMechanismContext(byte[] exportedContext)
throws GSSException {
return new Krb5Context(caller, exportedContext);
}
public final Oid getMechanismOid() {
return GSS_KRB5_MECH_OID;
}
public Provider getProvider() {
return PROVIDER;
}
public Oid[] getNameTypes() {
// nameTypes is cloned in GSSManager.getNamesForMech
return nameTypes;
}
private static Oid createOid(String oidStr) {
Oid retVal = null;
try {
retVal = new Oid(oidStr);
} catch (GSSException e) {
// Should not happen!
}
return retVal;
}
}

View file

@ -0,0 +1,348 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import sun.security.krb5.PrincipalName;
import sun.security.krb5.Realm;
import sun.security.krb5.KrbException;
import javax.security.auth.kerberos.ServicePermission;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.Provider;
import java.util.Locale;
/**
* Implements the GSSNameSpi for the krb5 mechanism.
*
* @author Mayank Upadhyay
*/
public class Krb5NameElement
implements GSSNameSpi {
private PrincipalName krb5PrincipalName;
private String gssNameStr = null;
private Oid gssNameType = null;
// XXX Move this concept into PrincipalName's asn1Encode() sometime
private static String CHAR_ENCODING = "UTF-8";
private Krb5NameElement(PrincipalName principalName,
String gssNameStr,
Oid gssNameType) {
this.krb5PrincipalName = principalName;
this.gssNameStr = gssNameStr;
this.gssNameType = gssNameType;
}
/**
* Instantiates a new Krb5NameElement object. Internally it stores the
* information provided by the input parameters so that they may later
* be used for output when a printable representaion of this name is
* needed in GSS-API format rather than in Kerberos format.
*
*/
static Krb5NameElement getInstance(String gssNameStr, Oid gssNameType)
throws GSSException {
/*
* A null gssNameType implies that the mechanism default
* Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL be used.
*/
if (gssNameType == null)
gssNameType = Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL;
else
if (!gssNameType.equals(GSSName.NT_USER_NAME) &&
!gssNameType.equals(GSSName.NT_HOSTBASED_SERVICE) &&
!gssNameType.equals(Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL) &&
!gssNameType.equals(GSSName.NT_EXPORT_NAME))
throw new GSSException(GSSException.BAD_NAMETYPE, -1,
gssNameType.toString()
+" is an unsupported nametype");
PrincipalName principalName;
try {
if (gssNameType.equals(GSSName.NT_EXPORT_NAME) ||
gssNameType.equals(Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL)) {
principalName = new PrincipalName(gssNameStr,
PrincipalName.KRB_NT_PRINCIPAL);
} else {
String[] components = getComponents(gssNameStr);
/*
* We have forms of GSS name strings that can come in:
*
* 1. names of the form "foo" with just one
* component. (This might include a "@" but only in escaped
* form like "\@")
* 2. names of the form "foo@bar" with two components
*
* The nametypes that are accepted are NT_USER_NAME, and
* NT_HOSTBASED_SERVICE.
*/
if (gssNameType.equals(GSSName.NT_USER_NAME))
principalName = new PrincipalName(gssNameStr,
PrincipalName.KRB_NT_PRINCIPAL);
else {
String hostName = null;
String service = components[0];
if (components.length >= 2)
hostName = components[1];
String principal = getHostBasedInstance(service, hostName);
principalName = new PrincipalName(principal,
PrincipalName.KRB_NT_SRV_HST);
}
}
} catch (KrbException e) {
throw new GSSException(GSSException.BAD_NAME, -1, e.getMessage());
}
if (principalName.isRealmDeduced() && !Realm.AUTODEDUCEREALM) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
sm.checkPermission(new ServicePermission(
"@" + principalName.getRealmAsString(), "-"));
} catch (SecurityException se) {
// Do not chain the actual exception to hide info
throw new GSSException(GSSException.FAILURE);
}
}
}
return new Krb5NameElement(principalName, gssNameStr, gssNameType);
}
static Krb5NameElement getInstance(PrincipalName principalName) {
return new Krb5NameElement(principalName,
principalName.getName(),
Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
}
private static String[] getComponents(String gssNameStr)
throws GSSException {
String[] retVal;
// XXX Perhaps provide this parsing code in PrincipalName
// Look for @ as in service@host
// Assumes host name will not have an escaped '@'
int separatorPos = gssNameStr.lastIndexOf('@', gssNameStr.length());
// Not really a separator if it is escaped. Then this is just part
// of the principal name or service name
if ((separatorPos > 0) &&
(gssNameStr.charAt(separatorPos-1) == '\\')) {
// Is the `\` character escaped itself?
if ((separatorPos - 2 < 0) ||
(gssNameStr.charAt(separatorPos-2) != '\\'))
separatorPos = -1;
}
if (separatorPos > 0) {
String serviceName = gssNameStr.substring(0, separatorPos);
String hostName = gssNameStr.substring(separatorPos+1);
retVal = new String[] { serviceName, hostName};
} else {
retVal = new String[] {gssNameStr};
}
return retVal;
}
private static String getHostBasedInstance(String serviceName,
String hostName)
throws GSSException {
StringBuffer temp = new StringBuffer(serviceName);
try {
// A lack of "@" defaults to the service being on the local
// host as per RFC 2743
// XXX Move this part into JGSS framework
if (hostName == null)
hostName = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
// use hostname as it is
}
hostName = hostName.toLowerCase(Locale.ENGLISH);
temp = temp.append('/').append(hostName);
return temp.toString();
}
public final PrincipalName getKrb5PrincipalName() {
return krb5PrincipalName;
}
/**
* Equal method for the GSSNameSpi objects.
* If either name denotes an anonymous principal, the call should
* return false.
*
* @param other to be compared with
* @return true if they both refer to the same entity, else false
* @exception GSSException with major codes of BAD_NAMETYPE,
* BAD_NAME, FAILURE
*/
public boolean equals(GSSNameSpi other) throws GSSException {
if (other == this)
return true;
if (other instanceof Krb5NameElement) {
Krb5NameElement that = (Krb5NameElement) other;
return (this.krb5PrincipalName.getName().equals(
that.krb5PrincipalName.getName()));
}
return false;
}
/**
* Compares this <code>GSSNameSpi</code> object to another Object
* that might be a <code>GSSNameSpi</code>. The behaviour is exactly
* the same as in {@link #equals(GSSNameSpi) equals} except that
* no GSSException is thrown; instead, false will be returned in the
* situation where an error occurs.
*
* @param another the object to be compared to
* @return true if they both refer to the same entity, else false
* @see #equals(GSSNameSpi)
*/
public boolean equals(Object another) {
if (this == another) {
return true;
}
try {
if (another instanceof Krb5NameElement)
return equals((Krb5NameElement) another);
} catch (GSSException e) {
// ignore exception
}
return false;
}
/**
* Returns a hashcode value for this GSSNameSpi.
*
* @return a hashCode value
*/
public int hashCode() {
return 37 * 17 + krb5PrincipalName.getName().hashCode();
}
/**
* Returns the principal name in the form user@REALM or
* host/service@REALM but with the following constraints that are
* imposed by RFC 1964:
* <pre>
* (1) all occurrences of the characters `@`, `/`, and `\` within
* principal components or realm names shall be quoted with an
* immediately-preceding `\`.
*
* (2) all occurrences of the null, backspace, tab, or newline
* characters within principal components or realm names will be
* represented, respectively, with `\0`, `\b`, `\t`, or `\n`.
*
* (3) the `\` quoting character shall not be emitted within an
* exported name except to accommodate cases (1) and (2).
* </pre>
*/
public byte[] export() throws GSSException {
// XXX Apply the above constraints.
byte[] retVal = null;
try {
retVal = krb5PrincipalName.getName().getBytes(CHAR_ENCODING);
} catch (UnsupportedEncodingException e) {
// Can't happen
}
return retVal;
}
/**
* Get the mechanism type that this NameElement corresponds to.
*
* @return the Oid of the mechanism type
*/
public Oid getMechanism() {
return (Krb5MechFactory.GSS_KRB5_MECH_OID);
}
/**
* Returns a string representation for this name. The printed
* name type can be obtained by calling getStringNameType().
*
* @return string form of this name
* @see #getStringNameType()
* @overrides Object#toString
*/
public String toString() {
return (gssNameStr);
// For testing: return (super.toString());
}
/**
* Returns the name type oid.
*/
public Oid getGSSNameType() {
return (gssNameType);
}
/**
* Returns the oid describing the format of the printable name.
*
* @return the Oid for the format of the printed name
*/
public Oid getStringNameType() {
// XXX For NT_EXPORT_NAME return a different name type. Infact,
// don't even store NT_EXPORT_NAME in the cons.
return (gssNameType);
}
/**
* Indicates if this name object represents an Anonymous name.
*/
public boolean isAnonymousName() {
return (gssNameType.equals(GSSName.NT_ANONYMOUS));
}
public Provider getProvider() {
return Krb5MechFactory.PROVIDER;
}
}

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import java.util.Date;
import sun.security.krb5.internal.Ticket;
/**
* Implements the krb5 proxy credential element used in constrained
* delegation. It is used in both impersonation (where there is no Kerberos 5
* communication between the middle server and the client) and normal
* constrained delegation (where there is, but client has not called
* requestCredDeleg(true)).
* @since 1.8
*/
public class Krb5ProxyCredential
implements Krb5CredElement {
public final Krb5InitCredential self; // the middle server
private final Krb5NameElement client; // the client
// The ticket with cname=client and sname=self. This can be a normal
// service ticket or an S4U2self ticket.
public final Ticket tkt;
Krb5ProxyCredential(Krb5InitCredential self, Krb5NameElement client,
Ticket tkt) {
this.self = self;
this.tkt = tkt;
this.client = client;
}
// The client name behind the proxy
@Override
public final Krb5NameElement getName() throws GSSException {
return client;
}
@Override
public int getInitLifetime() throws GSSException {
// endTime of tkt is not used by KDC, and it's also not
// available in the case of kerberos constr deleg
return self.getInitLifetime();
}
@Override
public int getAcceptLifetime() throws GSSException {
return 0;
}
@Override
public boolean isInitiatorCredential() throws GSSException {
return true;
}
@Override
public boolean isAcceptorCredential() throws GSSException {
return false;
}
@Override
public final Oid getMechanism() {
return Krb5MechFactory.GSS_KRB5_MECH_OID;
}
@Override
public final java.security.Provider getProvider() {
return Krb5MechFactory.PROVIDER;
}
@Override
public void dispose() throws GSSException {
try {
self.destroy();
} catch (javax.security.auth.DestroyFailedException e) {
GSSException gssException =
new GSSException(GSSException.FAILURE, -1,
"Could not destroy credentials - " + e.getMessage());
gssException.initCause(e);
}
}
@Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
// Cannot impersonate multiple levels without the impersonatee's TGT.
throw new GSSException(GSSException.FAILURE, -1,
"Only an initiate credentials can impersonate");
}
}

View file

@ -0,0 +1,118 @@
/*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import java.io.IOException;
import sun.security.util.*;
import sun.security.jgss.*;
/**
* This class represents a base class for all Kerberos v5 GSS-API
* tokens. It contains commonly used definitions and utilities.
*
* @author Mayank Upadhyay
*/
abstract class Krb5Token extends GSSToken {
/**
* The token id defined for the token emitted by the initSecContext call
* carrying the AP_REQ .
*/
public static final int AP_REQ_ID = 0x0100;
/**
* The token id defined for the token emitted by the acceptSecContext call
* carrying the AP_REP .
*/
public static final int AP_REP_ID = 0x0200;
/**
* The token id defined for any token carrying a KRB-ERR message.
*/
public static final int ERR_ID = 0x0300;
/**
* The token id defined for the token emitted by the getMIC call.
*/
public static final int MIC_ID = 0x0101;
/**
* The token id defined for the token emitted by the wrap call.
*/
public static final int WRAP_ID = 0x0201;
// new token ID draft-ietf-krb-wg-gssapi-cfx-07.txt
public static final int MIC_ID_v2 = 0x0404;
public static final int WRAP_ID_v2 = 0x0504;
/**
* The object identifier corresponding to the Kerberos v5 GSS-API
* mechanism.
*/
public static ObjectIdentifier OID;
static {
try {
OID = new ObjectIdentifier(Krb5MechFactory.
GSS_KRB5_MECH_OID.toString());
} catch (IOException ioe) {
// should not happen
}
}
/**
* Returns a strign representing the token type.
*
* @param tokenId the token id for which a string name is desired
* @return the String name of this token type
*/
public static String getTokenName(int tokenId) {
String retVal = null;
switch (tokenId) {
case AP_REQ_ID:
case AP_REP_ID:
retVal = "Context Establishment Token";
break;
case MIC_ID:
retVal = "MIC Token";
break;
case MIC_ID_v2:
retVal = "MIC Token (new format)";
break;
case WRAP_ID:
retVal = "Wrap Token";
break;
case WRAP_ID_v2:
retVal = "Wrap Token (new format)";
break;
default:
retVal = "Kerberos GSS-API Mechanism Token";
break;
}
return retVal;
}
}

View file

@ -0,0 +1,266 @@
/*
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KerberosKey;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KeyTab;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginException;
import java.security.AccessControlContext;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.GSSCaller;
import sun.security.krb5.Credentials;
import sun.security.krb5.EncryptionKey;
import sun.security.krb5.KrbException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import sun.security.krb5.KerberosSecrets;
import sun.security.krb5.PrincipalName;
/**
* Utilities for obtaining and converting Kerberos tickets.
*
*/
public class Krb5Util {
static final boolean DEBUG =
java.security.AccessController.doPrivileged(
new sun.security.action.GetBooleanAction
("sun.security.krb5.debug")).booleanValue();
/**
* Default constructor
*/
private Krb5Util() { // Cannot create one of these
}
/**
* Retrieve the service ticket for serverPrincipal from caller's Subject
* or from Subject obtained by logging in, or if not found, via the
* Ticket Granting Service using the TGT obtained from the Subject.
*
* Caller must have permission to:
* - access and update Subject's private credentials
* - create LoginContext
* - read the auth.login.defaultCallbackHandler security property
*
* NOTE: This method is used by JSSE Kerberos Cipher Suites
*/
public static KerberosTicket getTicketFromSubjectAndTgs(GSSCaller caller,
String clientPrincipal, String serverPrincipal, String tgsPrincipal,
AccessControlContext acc)
throws LoginException, KrbException, IOException {
// 1. Try to find service ticket in acc subject
Subject accSubj = Subject.getSubject(acc);
KerberosTicket ticket = SubjectComber.find(accSubj,
serverPrincipal, clientPrincipal, KerberosTicket.class);
if (ticket != null) {
return ticket; // found it
}
Subject loginSubj = null;
if (!GSSUtil.useSubjectCredsOnly(caller)) {
// 2. Try to get ticket from login
try {
loginSubj = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
ticket = SubjectComber.find(loginSubj,
serverPrincipal, clientPrincipal, KerberosTicket.class);
if (ticket != null) {
return ticket; // found it
}
} catch (LoginException e) {
// No login entry to use
// ignore and continue
}
}
// Service ticket not found in subject or login
// Try to get TGT to acquire service ticket
// 3. Try to get TGT from acc subject
KerberosTicket tgt = SubjectComber.find(accSubj,
tgsPrincipal, clientPrincipal, KerberosTicket.class);
boolean fromAcc;
if (tgt == null && loginSubj != null) {
// 4. Try to get TGT from login subject
tgt = SubjectComber.find(loginSubj,
tgsPrincipal, clientPrincipal, KerberosTicket.class);
fromAcc = false;
} else {
fromAcc = true;
}
// 5. Try to get service ticket using TGT
if (tgt != null) {
Credentials tgtCreds = ticketToCreds(tgt);
Credentials serviceCreds = Credentials.acquireServiceCreds(
serverPrincipal, tgtCreds);
if (serviceCreds != null) {
ticket = credsToTicket(serviceCreds);
// Store service ticket in acc's Subject
if (fromAcc && accSubj != null && !accSubj.isReadOnly()) {
accSubj.getPrivateCredentials().add(ticket);
}
}
}
return ticket;
}
/**
* Retrieves the ticket corresponding to the client/server principal
* pair from the Subject in the specified AccessControlContext.
* If the ticket can not be found in the Subject, and if
* useSubjectCredsOnly is false, then obtain ticket from
* a LoginContext.
*/
static KerberosTicket getTicket(GSSCaller caller,
String clientPrincipal, String serverPrincipal,
AccessControlContext acc) throws LoginException {
// Try to get ticket from acc's Subject
Subject accSubj = Subject.getSubject(acc);
KerberosTicket ticket =
SubjectComber.find(accSubj, serverPrincipal, clientPrincipal,
KerberosTicket.class);
// Try to get ticket from Subject obtained from GSSUtil
if (ticket == null && !GSSUtil.useSubjectCredsOnly(caller)) {
Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
ticket = SubjectComber.find(subject,
serverPrincipal, clientPrincipal, KerberosTicket.class);
}
return ticket;
}
/**
* Retrieves the caller's Subject, or Subject obtained by logging in
* via the specified caller.
*
* Caller must have permission to:
* - access the Subject
* - create LoginContext
* - read the auth.login.defaultCallbackHandler security property
*
* NOTE: This method is used by JSSE Kerberos Cipher Suites
*/
public static Subject getSubject(GSSCaller caller,
AccessControlContext acc) throws LoginException {
// Try to get the Subject from acc
Subject subject = Subject.getSubject(acc);
// Try to get Subject obtained from GSSUtil
if (subject == null && !GSSUtil.useSubjectCredsOnly(caller)) {
subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
}
return subject;
}
/**
* Retrieves the ServiceCreds for the specified server principal from
* the Subject in the specified AccessControlContext. If not found, and if
* useSubjectCredsOnly is false, then obtain from a LoginContext.
*
* NOTE: This method is also used by JSSE Kerberos Cipher Suites
*/
public static ServiceCreds getServiceCreds(GSSCaller caller,
String serverPrincipal, AccessControlContext acc)
throws LoginException {
Subject accSubj = Subject.getSubject(acc);
ServiceCreds sc = null;
if (accSubj != null) {
sc = ServiceCreds.getInstance(accSubj, serverPrincipal);
}
if (sc == null && !GSSUtil.useSubjectCredsOnly(caller)) {
Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
sc = ServiceCreds.getInstance(subject, serverPrincipal);
}
return sc;
}
public static KerberosTicket credsToTicket(Credentials serviceCreds) {
EncryptionKey sessionKey = serviceCreds.getSessionKey();
return new KerberosTicket(
serviceCreds.getEncoded(),
new KerberosPrincipal(serviceCreds.getClient().getName()),
new KerberosPrincipal(serviceCreds.getServer().getName(),
KerberosPrincipal.KRB_NT_SRV_INST),
sessionKey.getBytes(),
sessionKey.getEType(),
serviceCreds.getFlags(),
serviceCreds.getAuthTime(),
serviceCreds.getStartTime(),
serviceCreds.getEndTime(),
serviceCreds.getRenewTill(),
serviceCreds.getClientAddresses());
};
public static Credentials ticketToCreds(KerberosTicket kerbTicket)
throws KrbException, IOException {
return new Credentials(
kerbTicket.getEncoded(),
kerbTicket.getClient().getName(),
kerbTicket.getServer().getName(),
kerbTicket.getSessionKey().getEncoded(),
kerbTicket.getSessionKeyType(),
kerbTicket.getFlags(),
kerbTicket.getAuthTime(),
kerbTicket.getStartTime(),
kerbTicket.getEndTime(),
kerbTicket.getRenewTill(),
kerbTicket.getClientAddresses());
}
/**
* A helper method to get a sun..KeyTab from a javax..KeyTab
* @param ktab the javax..KeyTab object
* @return the sun..KeyTab object
*/
public static sun.security.krb5.internal.ktab.KeyTab
snapshotFromJavaxKeyTab(KeyTab ktab) {
return KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
.keyTabTakeSnapshot(ktab);
}
/**
* A helper method to get EncryptionKeys from a javax..KeyTab
* @param ktab the javax..KeyTab object
* @param cname the PrincipalName
* @return the EKeys, never null, might be empty
*/
public static EncryptionKey[] keysFromJavaxKeyTab(
KeyTab ktab, PrincipalName cname) {
return snapshotFromJavaxKeyTab(ktab).readServiceKeys(cname);
}
}

View file

@ -0,0 +1,721 @@
/*
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.jgss.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.security.MessageDigest;
/**
* This class is a base class for other token definitions that pertain to
* per-message GSS-API calls. Conceptually GSS-API has two types of
* per-message tokens: WrapToken and MicToken. They differ in the respect
* that a WrapToken carries additional plaintext or ciphertext application
* data besides just the sequence number and checksum. This class
* encapsulates the commonality in the structure of the WrapToken and the
* MicToken. This structure can be represented as:
* <p>
* <pre>
* 0..1 TOK_ID Identification field.
* 01 01 - Mic token
* 02 01 - Wrap token
* 2..3 SGN_ALG Checksum algorithm indicator.
* 00 00 - DES MAC MD5
* 01 00 - MD2.5
* 02 00 - DES MAC
* 04 00 - HMAC SHA1 DES3-KD
* 11 00 - RC4-HMAC
* 4..5 SEAL_ALG ff ff - none
* 00 00 - DES
* 02 00 - DES3-KD
* 10 00 - RC4-HMAC
* 6..7 Filler Contains ff ff
* 8..15 SND_SEQ Encrypted sequence number field.
* 16..s+15 SGN_CKSUM Checksum of plaintext padded data,
* calculated according to algorithm
* specified in SGN_ALG field.
* s+16..last Data encrypted or plaintext padded data
* </pre>
* Where "s" indicates the size of the checksum.
* <p>
* As always, this is preceeded by a GSSHeader.
*
* @author Mayank Upadhyay
* @author Ram Marti
* @see sun.security.jgss.GSSHeader
*/
abstract class MessageToken extends Krb5Token {
/* Fields in header minus checksum size */
private static final int TOKEN_NO_CKSUM_SIZE = 16;
/**
* Filler data as defined in the specification of the Kerberos v5 GSS-API
* Mechanism.
*/
private static final int FILLER = 0xffff;
// Signing algorithm values (for the SNG_ALG field)
// From RFC 1964
/* Use a DES MAC MD5 checksum */
static final int SGN_ALG_DES_MAC_MD5 = 0x0000;
/* Use DES MAC checksum. */
static final int SGN_ALG_DES_MAC = 0x0200;
// From draft-raeburn-cat-gssapi-krb5-3des-00
/* Use a HMAC SHA1 DES3 -KD checksum */
static final int SGN_ALG_HMAC_SHA1_DES3_KD = 0x0400;
// Sealing algorithm values (for the SEAL_ALG field)
// RFC 1964
/**
* A value for the SEAL_ALG field that indicates that no encryption was
* used.
*/
static final int SEAL_ALG_NONE = 0xffff;
/* Use DES CBC encryption algorithm. */
static final int SEAL_ALG_DES = 0x0000;
// From draft-raeburn-cat-gssapi-krb5-3des-00
/**
* Use DES3-KD sealing algorithm. (draft-raeburn-cat-gssapi-krb5-3des-00)
* This algorithm uses triple-DES with key derivation, with a usage
* value KG_USAGE_SEAL. Padding is still to 8-byte multiples, and the
* IV for encrypting application data is zero.
*/
static final int SEAL_ALG_DES3_KD = 0x0200;
// draft draft-brezak-win2k-krb-rc4-hmac-04.txt
static final int SEAL_ALG_ARCFOUR_HMAC = 0x1000;
static final int SGN_ALG_HMAC_MD5_ARCFOUR = 0x1100;
private static final int TOKEN_ID_POS = 0;
private static final int SIGN_ALG_POS = 2;
private static final int SEAL_ALG_POS = 4;
private int seqNumber;
private boolean confState = true;
private boolean initiator = true;
private int tokenId = 0;
private GSSHeader gssHeader = null;
private MessageTokenHeader tokenHeader = null;
private byte[] checksum = null;
private byte[] encSeqNumber = null;
private byte[] seqNumberData = null;
/* cipher instance used by the corresponding GSSContext */
CipherHelper cipherHelper = null;
/**
* Constructs a MessageToken from a byte array. If there are more bytes
* in the array than needed, the extra bytes are simply ignroed.
*
* @param tokenId the token id that should be contained in this token as
* it is read.
* @param context the Kerberos context associated with this token
* @param tokenBytes the byte array containing the token
* @param tokenOffset the offset where the token begins
* @param tokenLen the length of the token
* @param prop the MessageProp structure in which the properties of the
* token should be stored.
* @throws GSSException if there is a problem parsing the token
*/
MessageToken(int tokenId, Krb5Context context,
byte[] tokenBytes, int tokenOffset, int tokenLen,
MessageProp prop) throws GSSException {
this(tokenId, context,
new ByteArrayInputStream(tokenBytes, tokenOffset, tokenLen),
prop);
}
/**
* Constructs a MessageToken from an InputStream. Bytes will be read on
* demand and the thread might block if there are not enough bytes to
* complete the token.
*
* @param tokenId the token id that should be contained in this token as
* it is read.
* @param context the Kerberos context associated with this token
* @param is the InputStream from which to read
* @param prop the MessageProp structure in which the properties of the
* token should be stored.
* @throws GSSException if there is a problem reading from the
* InputStream or parsing the token
*/
MessageToken(int tokenId, Krb5Context context, InputStream is,
MessageProp prop) throws GSSException {
init(tokenId, context);
try {
gssHeader = new GSSHeader(is);
if (!gssHeader.getOid().equals(OID)) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
getTokenName(tokenId));
}
if (!confState) {
prop.setPrivacy(false);
}
tokenHeader = new MessageTokenHeader(is, prop);
encSeqNumber = new byte[8];
readFully(is, encSeqNumber);
// debug("\n\tRead EncSeq#=" +
// getHexBytes(encSeqNumber, encSeqNumber.length));
checksum = new byte[cipherHelper.getChecksumLength()];
readFully(is, checksum);
// debug("\n\tRead checksum=" +
// getHexBytes(checksum, checksum.length));
// debug("\nLeaving MessageToken.Cons\n");
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
getTokenName(tokenId) + ":" + e.getMessage());
}
}
/**
* Used to obtain the GSSHeader that was at the start of this
* token.
*/
public final GSSHeader getGSSHeader() {
return gssHeader;
}
/**
* Used to obtain the token id that was contained in this token.
* @return the token id in the token
*/
public final int getTokenId() {
return tokenId;
}
/**
* Used to obtain the encrypted sequence number in this token.
* @return the encrypted sequence number in the token
*/
public final byte[] getEncSeqNumber() {
return encSeqNumber;
}
/**
* Used to obtain the checksum that was contained in this token.
* @return the checksum in the token
*/
public final byte[] getChecksum() {
return checksum;
}
/**
* Used to determine if this token contains any encrypted data.
* @return true if it contains any encrypted data, false if there is only
* plaintext data or if there is no data.
*/
public final boolean getConfState() {
return confState;
}
/**
* Generates the checksum field and the encrypted sequence number
* field. The encrypted sequence number uses the 8 bytes of the checksum
* as an initial vector in a fixed DesCbc algorithm.
*
* @param prop the MessageProp structure that determines what sort of
* checksum and sealing algorithm should be used. The lower byte
* of qop determines the checksum algorithm while the upper byte
* determines the signing algorithm.
* Checksum values are:
* 0 - default (DES_MAC)
* 1 - MD5
* 2 - DES_MD5
* 3 - DES_MAC
* 4 - HMAC_SHA1
* Sealing values are:
* 0 - default (DES)
* 1 - DES
* 2 - DES3-KD
*
* @param optionalHeader an optional header that will be processed first
* during checksum calculation
*
* @param data the application data to checksum
* @param offset the offset where the data starts
* @param len the length of the data
*
* @param optionalTrailer an optional trailer that will be processed
* last during checksum calculation. e.g., padding that should be
* appended to the application data
*
* @throws GSSException if an error occurs in the checksum calculation or
* encryption sequence number calculation.
*/
public void genSignAndSeqNumber(MessageProp prop,
byte[] optionalHeader,
byte[] data, int offset, int len,
byte[] optionalTrailer)
throws GSSException {
// debug("Inside MessageToken.genSignAndSeqNumber:\n");
int qop = prop.getQOP();
if (qop != 0) {
qop = 0;
prop.setQOP(qop);
}
if (!confState) {
prop.setPrivacy(false);
}
// Create a token header with the correct sign and seal algorithm
// values.
tokenHeader =
new MessageTokenHeader(tokenId, prop.getPrivacy(), qop);
// Calculate SGN_CKSUM
checksum =
getChecksum(optionalHeader, data, offset, len, optionalTrailer);
// debug("\n\tCalc checksum=" +
// getHexBytes(checksum, checksum.length));
// Calculate SND_SEQ
seqNumberData = new byte[8];
// When using this RC4 based encryption type, the sequence number is
// always sent in big-endian rather than little-endian order.
if (cipherHelper.isArcFour()) {
writeBigEndian(seqNumber, seqNumberData);
} else {
// for all other etypes
writeLittleEndian(seqNumber, seqNumberData);
}
if (!initiator) {
seqNumberData[4] = (byte)0xff;
seqNumberData[5] = (byte)0xff;
seqNumberData[6] = (byte)0xff;
seqNumberData[7] = (byte)0xff;
}
encSeqNumber = cipherHelper.encryptSeq(checksum, seqNumberData, 0, 8);
// debug("\n\tCalc seqNum=" +
// getHexBytes(seqNumberData, seqNumberData.length));
// debug("\n\tCalc encSeqNum=" +
// getHexBytes(encSeqNumber, encSeqNumber.length));
}
/**
* Verifies that the checksum field and sequence number direction bytes
* are valid and consistent with the application data.
*
* @param optionalHeader an optional header that will be processed first
* during checksum calculation.
*
* @param data the application data
* @param offset the offset where the data begins
* @param len the length of the application data
*
* @param optionalTrailer an optional trailer that will be processed last
* during checksum calculation. e.g., padding that should be appended to
* the application data
*
* @throws GSSException if an error occurs in the checksum calculation or
* encryption sequence number calculation.
*/
public final boolean verifySignAndSeqNumber(byte[] optionalHeader,
byte[] data, int offset, int len,
byte[] optionalTrailer)
throws GSSException {
// debug("\tIn verifySign:\n");
// debug("\t\tchecksum: [" + getHexBytes(checksum) + "]\n");
byte[] myChecksum =
getChecksum(optionalHeader, data, offset, len, optionalTrailer);
// debug("\t\tmychecksum: [" + getHexBytes(myChecksum) +"]\n");
// debug("\t\tchecksum: [" + getHexBytes(checksum) + "]\n");
if (MessageDigest.isEqual(checksum, myChecksum)) {
seqNumberData = cipherHelper.decryptSeq(
checksum, encSeqNumber, 0, 8);
// debug("\t\tencSeqNumber: [" + getHexBytes(encSeqNumber)
// + "]\n");
// debug("\t\tseqNumberData: [" + getHexBytes(seqNumberData)
// + "]\n");
/*
* The token from the initiator has direction bytes 0x00 and
* the token from the acceptor has direction bytes 0xff.
*/
byte directionByte = 0;
if (initiator)
directionByte = (byte) 0xff; // Received token from acceptor
if ((seqNumberData[4] == directionByte) &&
(seqNumberData[5] == directionByte) &&
(seqNumberData[6] == directionByte) &&
(seqNumberData[7] == directionByte))
return true;
}
return false;
}
public final int getSequenceNumber() {
int sequenceNum = 0;
if (cipherHelper.isArcFour()) {
sequenceNum = readBigEndian(seqNumberData, 0, 4);
} else {
sequenceNum = readLittleEndian(seqNumberData, 0, 4);
}
return sequenceNum;
}
/**
* Computes the checksum based on the algorithm stored in the
* tokenHeader.
*
* @param optionalHeader an optional header that will be processed first
* during checksum calculation.
*
* @param data the application data
* @param offset the offset where the data begins
* @param len the length of the application data
*
* @param optionalTrailer an optional trailer that will be processed last
* during checksum calculation. e.g., padding that should be appended to
* the application data
*
* @throws GSSException if an error occurs in the checksum calculation.
*/
private byte[] getChecksum(byte[] optionalHeader,
byte[] data, int offset, int len,
byte[] optionalTrailer)
throws GSSException {
// debug("Will do getChecksum:\n");
/*
* For checksum calculation the token header bytes i.e., the first 8
* bytes following the GSSHeader, are logically prepended to the
* application data to bind the data to this particular token.
*
* Note: There is no such requirement wrt adding padding to the
* application data for checksumming, although the cryptographic
* algorithm used might itself apply some padding.
*/
byte[] tokenHeaderBytes = tokenHeader.getBytes();
byte[] existingHeader = optionalHeader;
byte[] checksumDataHeader = tokenHeaderBytes;
if (existingHeader != null) {
checksumDataHeader = new byte[tokenHeaderBytes.length +
existingHeader.length];
System.arraycopy(tokenHeaderBytes, 0,
checksumDataHeader, 0, tokenHeaderBytes.length);
System.arraycopy(existingHeader, 0,
checksumDataHeader, tokenHeaderBytes.length,
existingHeader.length);
}
return cipherHelper.calculateChecksum(tokenHeader.getSignAlg(),
checksumDataHeader, optionalTrailer, data, offset, len, tokenId);
}
/**
* Constructs an empty MessageToken for the local context to send to
* the peer. It also increments the local sequence number in the
* Krb5Context instance it uses after obtaining the object lock for
* it.
*
* @param tokenId the token id that should be contained in this token
* @param context the Kerberos context associated with this token
*/
MessageToken(int tokenId, Krb5Context context) throws GSSException {
/*
debug("\n============================");
debug("\nMySessionKey=" +
getHexBytes(context.getMySessionKey().getBytes()));
debug("\nPeerSessionKey=" +
getHexBytes(context.getPeerSessionKey().getBytes()));
debug("\n============================\n");
*/
init(tokenId, context);
this.seqNumber = context.incrementMySequenceNumber();
}
private void init(int tokenId, Krb5Context context) throws GSSException {
this.tokenId = tokenId;
// Just for consistency check in Wrap
this.confState = context.getConfState();
this.initiator = context.isInitiator();
this.cipherHelper = context.getCipherHelper(null);
// debug("In MessageToken.Cons");
}
/**
* Encodes a GSSHeader and this token onto an OutputStream.
*
* @param os the OutputStream to which this should be written
* @throws GSSException if an error occurs while writing to the OutputStream
*/
public void encode(OutputStream os) throws IOException, GSSException {
gssHeader = new GSSHeader(OID, getKrb5TokenSize());
gssHeader.encode(os);
tokenHeader.encode(os);
// debug("Writing seqNumber: " + getHexBytes(encSeqNumber));
os.write(encSeqNumber);
// debug("Writing checksum: " + getHexBytes(checksum));
os.write(checksum);
}
/**
* Obtains the size of this token. Note that this excludes the size of
* the GSSHeader.
* @return token size
*/
protected int getKrb5TokenSize() throws GSSException {
return getTokenSize();
}
protected final int getTokenSize() throws GSSException {
return TOKEN_NO_CKSUM_SIZE + cipherHelper.getChecksumLength();
}
protected static final int getTokenSize(CipherHelper ch)
throws GSSException {
return TOKEN_NO_CKSUM_SIZE + ch.getChecksumLength();
}
/**
* Obtains the conext key that is associated with this token.
* @return the context key
*/
/*
public final byte[] getContextKey() {
return contextKey;
}
*/
/**
* Obtains the encryption algorithm that should be used in this token
* given the state of confidentiality the application requested.
* Requested qop must be consistent with negotiated session key.
* @param confRequested true if the application desired confidentiality
* on this token, false otherwise
* @param qop the qop requested by the application
* @throws GSSException if qop is incompatible with the negotiated
* session key
*/
protected abstract int getSealAlg(boolean confRequested, int qop)
throws GSSException;
// ******************************************* //
// I N N E R C L A S S E S F O L L O W
// ******************************************* //
/**
* This inner class represents the initial portion of the message token
* and contains information about the checksum and encryption algorithms
* that are in use. It constitutes the first 8 bytes of the
* message token:
* <pre>
* 0..1 TOK_ID Identification field.
* 01 01 - Mic token
* 02 01 - Wrap token
* 2..3 SGN_ALG Checksum algorithm indicator.
* 00 00 - DES MAC MD5
* 01 00 - MD2.5
* 02 00 - DES MAC
* 04 00 - HMAC SHA1 DES3-KD
* 11 00 - RC4-HMAC
* 4..5 SEAL_ALG ff ff - none
* 00 00 - DES
* 02 00 - DES3-KD
* 10 00 - RC4-HMAC
* 6..7 Filler Contains ff ff
* </pre>
*/
class MessageTokenHeader {
private int tokenId;
private int signAlg;
private int sealAlg;
private byte[] bytes = new byte[8];
/**
* Constructs a MessageTokenHeader for the specified token type with
* appropriate checksum and encryption algorithms fields.
*
* @param tokenId the token id for this message token
* @param conf true if confidentiality will be resuested with this
* message token, false otherwise.
* @param qop the value of the quality of protection that will be
* desired.
*/
public MessageTokenHeader(int tokenId, boolean conf, int qop)
throws GSSException {
this.tokenId = tokenId;
signAlg = MessageToken.this.getSgnAlg(qop);
sealAlg = MessageToken.this.getSealAlg(conf, qop);
bytes[0] = (byte) (tokenId >>> 8);
bytes[1] = (byte) (tokenId);
bytes[2] = (byte) (signAlg >>> 8);
bytes[3] = (byte) (signAlg);
bytes[4] = (byte) (sealAlg >>> 8);
bytes[5] = (byte) (sealAlg);
bytes[6] = (byte) (MessageToken.FILLER >>> 8);
bytes[7] = (byte) (MessageToken.FILLER);
}
/**
* Constructs a MessageTokenHeader by reading it from an InputStream
* and sets the appropriate confidentiality and quality of protection
* values in a MessageProp structure.
*
* @param is the InputStream to read from
* @param prop the MessageProp to populate
* @throws IOException is an error occurs while reading from the
* InputStream
*/
public MessageTokenHeader(InputStream is, MessageProp prop)
throws IOException {
readFully(is, bytes);
tokenId = readInt(bytes, TOKEN_ID_POS);
signAlg = readInt(bytes, SIGN_ALG_POS);
sealAlg = readInt(bytes, SEAL_ALG_POS);
// debug("\nMessageTokenHeader read tokenId=" +
// getHexBytes(bytes) + "\n");
// XXX compare to FILLER
int temp = readInt(bytes, SEAL_ALG_POS + 2);
// debug("SIGN_ALG=" + signAlg);
switch (sealAlg) {
case SEAL_ALG_DES:
case SEAL_ALG_DES3_KD:
case SEAL_ALG_ARCFOUR_HMAC:
prop.setPrivacy(true);
break;
default:
prop.setPrivacy(false);
}
prop.setQOP(0); // default
}
/**
* Encodes this MessageTokenHeader onto an OutputStream
* @param os the OutputStream to write to
* @throws IOException is an error occurs while writing
*/
public final void encode(OutputStream os) throws IOException {
os.write(bytes);
}
/**
* Returns the token id for the message token.
* @return the token id
* @see sun.security.jgss.krb5.Krb5Token#MIC_ID
* @see sun.security.jgss.krb5.Krb5Token#WRAP_ID
*/
public final int getTokenId() {
return tokenId;
}
/**
* Returns the sign algorithm for the message token.
* @return the sign algorithm
* @see sun.security.jgss.krb5.MessageToken#SIGN_DES_MAC
* @see sun.security.jgss.krb5.MessageToken#SIGN_DES_MAC_MD5
*/
public final int getSignAlg() {
return signAlg;
}
/**
* Returns the seal algorithm for the message token.
* @return the seal algorithm
* @see sun.security.jgss.krb5.MessageToken#SEAL_ALG_DES
* @see sun.security.jgss.krb5.MessageToken#SEAL_ALG_NONE
*/
public final int getSealAlg() {
return sealAlg;
}
/**
* Returns the bytes of this header.
* @return 8 bytes that form this header
*/
public final byte[] getBytes() {
return bytes;
}
} // end of class MessageTokenHeader
/**
* Determine signing algorithm based on QOP.
*/
protected int getSgnAlg(int qop) throws GSSException {
// QOP ignored
return cipherHelper.getSgnAlg();
}
}

View file

@ -0,0 +1,643 @@
/*
* Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.security.MessageDigest;
import java.util.Arrays;
/**
* This class is a base class for new GSS token definitions, as defined
* in RFC 4121, that pertain to per-message GSS-API calls. Conceptually
* GSS-API has two types of per-message tokens: WrapToken and MicToken.
* They differ in the respect that a WrapToken carries additional plaintext
* or ciphertext application data besides just the sequence number and
* checksum. This class encapsulates the commonality in the structure of
* the WrapToken and the MicToken. This structure can be represented as:
* <p>
* <pre>
* Wrap Tokens
*
* Octet no Name Description
* ---------------------------------------------------------------
* 0..1 TOK_ID Identification field. Tokens emitted by
* GSS_Wrap() contain the hex value 05 04
* expressed in big-endian order in this field.
* 2 Flags Attributes field, as described in section
* 4.2.2.
* 3 Filler Contains the hex value FF.
* 4..5 EC Contains the "extra count" field, in big-
* endian order as described in section 4.2.3.
* 6..7 RRC Contains the "right rotation count" in big
* endian order, as described in section 4.2.5.
* 8..15 SND_SEQ Sequence number field in clear text,
* expressed in big-endian order.
* 16..last Data Encrypted data for Wrap tokens with
* confidentiality, or plaintext data followed
* by the checksum for Wrap tokens without
* confidentiality, as described in section
* 4.2.4.
* MIC Tokens
*
* Octet no Name Description
* -----------------------------------------------------------------
* 0..1 TOK_ID Identification field. Tokens emitted by
* GSS_GetMIC() contain the hex value 04 04
* expressed in big-endian order in this field.
* 2 Flags Attributes field, as described in section
* 4.2.2.
* 3..7 Filler Contains five octets of hex value FF.
* 8..15 SND_SEQ Sequence number field in clear text,
* expressed in big-endian order.
* 16..last SGN_CKSUM Checksum of the "to-be-signed" data and
* octet 0..15, as described in section 4.2.4.
*
* </pre>
* <p>
* This class is the super class of WrapToken_v2 and MicToken_v2. The token's
* header (bytes[0..15]) and data (byte[16..]) are saved in tokenHeader and
* tokenData fields. Since there is no easy way to find out the exact length
* of a WrapToken_v2 token from any header info, in the case of reading from
* stream, we read all available() bytes into the token.
* <p>
* All read actions are performed in this super class. On the write part, the
* super class only write the tokenHeader, and the content writing is inside
* child classes.
*
* @author Seema Malkani
*/
abstract class MessageToken_v2 extends Krb5Token {
protected static final int TOKEN_HEADER_SIZE = 16;
private static final int TOKEN_ID_POS = 0;
private static final int TOKEN_FLAG_POS = 2;
private static final int TOKEN_EC_POS = 4;
private static final int TOKEN_RRC_POS = 6;
/**
* The size of the random confounder used in a WrapToken.
*/
protected static final int CONFOUNDER_SIZE = 16;
// RFC 4121, key usage values
static final int KG_USAGE_ACCEPTOR_SEAL = 22;
static final int KG_USAGE_ACCEPTOR_SIGN = 23;
static final int KG_USAGE_INITIATOR_SEAL = 24;
static final int KG_USAGE_INITIATOR_SIGN = 25;
// RFC 4121, Flags Field
private static final int FLAG_SENDER_IS_ACCEPTOR = 1;
private static final int FLAG_WRAP_CONFIDENTIAL = 2;
private static final int FLAG_ACCEPTOR_SUBKEY = 4;
private static final int FILLER = 0xff;
private MessageTokenHeader tokenHeader = null;
// Common field
private int tokenId = 0;
private int seqNumber;
protected byte[] tokenData; // content of token, without the header
protected int tokenDataLen;
// Key usage number for crypto action
private int key_usage = 0;
// EC and RRC fields, WrapToken only
private int ec = 0;
private int rrc = 0;
// Checksum. Always in MicToken, might be in WrapToken
byte[] checksum = null;
// Context properties
private boolean confState = true;
private boolean initiator = true;
private boolean have_acceptor_subkey = false;
/* cipher instance used by the corresponding GSSContext */
CipherHelper cipherHelper = null;
/**
* Constructs a MessageToken from a byte array.
*
* @param tokenId the token id that should be contained in this token as
* it is read.
* @param context the Kerberos context associated with this token
* @param tokenBytes the byte array containing the token
* @param tokenOffset the offset where the token begins
* @param tokenLen the length of the token
* @param prop the MessageProp structure in which the properties of the
* token should be stored.
* @throws GSSException if there is a problem parsing the token
*/
MessageToken_v2(int tokenId, Krb5Context context,
byte[] tokenBytes, int tokenOffset, int tokenLen,
MessageProp prop) throws GSSException {
this(tokenId, context,
new ByteArrayInputStream(tokenBytes, tokenOffset, tokenLen),
prop);
}
/**
* Constructs a MessageToken from an InputStream. Bytes will be read on
* demand and the thread might block if there are not enough bytes to
* complete the token. Please note there is no accurate way to find out
* the size of a token, but we try our best to make sure there is
* enough bytes to construct one.
*
* @param tokenId the token id that should be contained in this token as
* it is read.
* @param context the Kerberos context associated with this token
* @param is the InputStream from which to read
* @param prop the MessageProp structure in which the properties of the
* token should be stored.
* @throws GSSException if there is a problem reading from the
* InputStream or parsing the token
*/
MessageToken_v2(int tokenId, Krb5Context context, InputStream is,
MessageProp prop) throws GSSException {
init(tokenId, context);
try {
if (!confState) {
prop.setPrivacy(false);
}
tokenHeader = new MessageTokenHeader(is, prop, tokenId);
// set key_usage
if (tokenId == Krb5Token.WRAP_ID_v2) {
key_usage = (!initiator ? KG_USAGE_INITIATOR_SEAL
: KG_USAGE_ACCEPTOR_SEAL);
} else if (tokenId == Krb5Token.MIC_ID_v2) {
key_usage = (!initiator ? KG_USAGE_INITIATOR_SIGN
: KG_USAGE_ACCEPTOR_SIGN);
}
int minSize = 0; // minimal size for token data
if (tokenId == Krb5Token.WRAP_ID_v2 && prop.getPrivacy()) {
minSize = CONFOUNDER_SIZE +
TOKEN_HEADER_SIZE + cipherHelper.getChecksumLength();
} else {
minSize = cipherHelper.getChecksumLength();
}
// Read token data
if (tokenId == Krb5Token.MIC_ID_v2) {
// The only case we can precisely predict the token data length
tokenDataLen = minSize;
tokenData = new byte[minSize];
readFully(is, tokenData);
} else {
tokenDataLen = is.available();
if (tokenDataLen >= minSize) { // read in one shot
tokenData = new byte[tokenDataLen];
readFully(is, tokenData);
} else {
byte[] tmp = new byte[minSize];
readFully(is, tmp);
// Hope while blocked in the read above, more data would
// come and is.available() below contains the whole token.
int more = is.available();
tokenDataLen = minSize + more;
tokenData = Arrays.copyOf(tmp, tokenDataLen);
readFully(is, tokenData, minSize, more);
}
}
if (tokenId == Krb5Token.WRAP_ID_v2) {
rotate();
}
if (tokenId == Krb5Token.MIC_ID_v2 ||
(tokenId == Krb5Token.WRAP_ID_v2 && !prop.getPrivacy())) {
// Read checksum
int chkLen = cipherHelper.getChecksumLength();
checksum = new byte[chkLen];
System.arraycopy(tokenData, tokenDataLen-chkLen,
checksum, 0, chkLen);
// validate EC for Wrap tokens without confidentiality
if (tokenId == Krb5Token.WRAP_ID_v2 && !prop.getPrivacy()) {
if (chkLen != ec) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
getTokenName(tokenId) + ":" + "EC incorrect!");
}
}
}
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
getTokenName(tokenId) + ":" + e.getMessage());
}
}
/**
* Used to obtain the token id that was contained in this token.
* @return the token id in the token
*/
public final int getTokenId() {
return tokenId;
}
/**
* Used to obtain the key_usage type for this token.
* @return the key_usage for the token
*/
public final int getKeyUsage() {
return key_usage;
}
/**
* Used to determine if this token contains any encrypted data.
* @return true if it contains any encrypted data, false if there is only
* plaintext data or if there is no data.
*/
public final boolean getConfState() {
return confState;
}
/**
* Generates the checksum field and the sequence number field.
*
* @param prop the MessageProp structure
* @param data the application data to checksum
* @param offset the offset where the data starts
* @param len the length of the data
*
* @throws GSSException if an error occurs in the checksum calculation or
* sequence number calculation.
*/
public void genSignAndSeqNumber(MessageProp prop,
byte[] data, int offset, int len)
throws GSSException {
// debug("Inside MessageToken.genSignAndSeqNumber:\n");
int qop = prop.getQOP();
if (qop != 0) {
qop = 0;
prop.setQOP(qop);
}
if (!confState) {
prop.setPrivacy(false);
}
// Create a new gss token header as defined in RFC 4121
tokenHeader = new MessageTokenHeader(tokenId, prop.getPrivacy());
// debug("\n\t Message Header = " +
// getHexBytes(tokenHeader.getBytes(), tokenHeader.getBytes().length));
// set key_usage
if (tokenId == Krb5Token.WRAP_ID_v2) {
key_usage = (initiator ? KG_USAGE_INITIATOR_SEAL
: KG_USAGE_ACCEPTOR_SEAL);
} else if (tokenId == Krb5Token.MIC_ID_v2) {
key_usage = (initiator ? KG_USAGE_INITIATOR_SIGN
: KG_USAGE_ACCEPTOR_SIGN);
}
// Calculate SGN_CKSUM
if ((tokenId == MIC_ID_v2) ||
(!prop.getPrivacy() && (tokenId == WRAP_ID_v2))) {
checksum = getChecksum(data, offset, len);
// debug("\n\tCalc checksum=" +
// getHexBytes(checksum, checksum.length));
}
// In Wrap tokens without confidentiality, the EC field SHALL be used
// to encode the number of octets in the trailing checksum
if (!prop.getPrivacy() && (tokenId == WRAP_ID_v2)) {
byte[] tok_header = tokenHeader.getBytes();
tok_header[4] = (byte) (checksum.length >>> 8);
tok_header[5] = (byte) (checksum.length);
}
}
/**
* Verifies the validity of checksum field
*
* @param data the application data
* @param offset the offset where the data begins
* @param len the length of the application data
*
* @throws GSSException if an error occurs in the checksum calculation
*/
public final boolean verifySign(byte[] data, int offset, int len)
throws GSSException {
// debug("\t====In verifySign:====\n");
// debug("\t\t checksum: [" + getHexBytes(checksum) + "]\n");
// debug("\t\t data = [" + getHexBytes(data) + "]\n");
byte[] myChecksum = getChecksum(data, offset, len);
// debug("\t\t mychecksum: [" + getHexBytes(myChecksum) +"]\n");
if (MessageDigest.isEqual(checksum, myChecksum)) {
// debug("\t\t====Checksum PASS:====\n");
return true;
}
return false;
}
/**
* Rotate bytes as per the "RRC" (Right Rotation Count) received.
* Our implementation does not do any rotates when sending, only
* when receiving, we rotate left as per the RRC count, to revert it.
*/
private void rotate() {
if (rrc % tokenDataLen != 0) {
rrc = rrc % tokenDataLen;
byte[] newBytes = new byte[tokenDataLen];
System.arraycopy(tokenData, rrc, newBytes, 0, tokenDataLen-rrc);
System.arraycopy(tokenData, 0, newBytes, tokenDataLen-rrc, rrc);
tokenData = newBytes;
}
}
public final int getSequenceNumber() {
return seqNumber;
}
/**
* Computes the checksum based on the algorithm stored in the
* tokenHeader.
*
* @param data the application data
* @param offset the offset where the data begins
* @param len the length of the application data
*
* @throws GSSException if an error occurs in the checksum calculation.
*/
byte[] getChecksum(byte[] data, int offset, int len)
throws GSSException {
// debug("Will do getChecksum:\n");
/*
* For checksum calculation the token header bytes i.e., the first 16
* bytes following the GSSHeader, are logically prepended to the
* application data to bind the data to this particular token.
*
* Note: There is no such requirement wrt adding padding to the
* application data for checksumming, although the cryptographic
* algorithm used might itself apply some padding.
*/
byte[] tokenHeaderBytes = tokenHeader.getBytes();
// check confidentiality
int conf_flag = tokenHeaderBytes[TOKEN_FLAG_POS] &
FLAG_WRAP_CONFIDENTIAL;
// clear EC and RRC in token header for checksum calculation
if ((conf_flag == 0) && (tokenId == WRAP_ID_v2)) {
tokenHeaderBytes[4] = 0;
tokenHeaderBytes[5] = 0;
tokenHeaderBytes[6] = 0;
tokenHeaderBytes[7] = 0;
}
return cipherHelper.calculateChecksum(tokenHeaderBytes, data,
offset, len, key_usage);
}
/**
* Constructs an empty MessageToken for the local context to send to
* the peer. It also increments the local sequence number in the
* Krb5Context instance it uses after obtaining the object lock for
* it.
*
* @param tokenId the token id that should be contained in this token
* @param context the Kerberos context associated with this token
*/
MessageToken_v2(int tokenId, Krb5Context context) throws GSSException {
/*
debug("\n============================");
debug("\nMySessionKey=" +
getHexBytes(context.getMySessionKey().getBytes()));
debug("\nPeerSessionKey=" +
getHexBytes(context.getPeerSessionKey().getBytes()));
debug("\n============================\n");
*/
init(tokenId, context);
this.seqNumber = context.incrementMySequenceNumber();
}
private void init(int tokenId, Krb5Context context) throws GSSException {
this.tokenId = tokenId;
// Just for consistency check in Wrap
this.confState = context.getConfState();
this.initiator = context.isInitiator();
this.have_acceptor_subkey = context.getKeySrc() == Krb5Context.ACCEPTOR_SUBKEY;
this.cipherHelper = context.getCipherHelper(null);
// debug("In MessageToken.Cons");
}
/**
* Encodes a MessageTokenHeader onto an OutputStream.
*
* @param os the OutputStream to which this should be written
* @throws IOException is an error occurs while writing to the OutputStream
*/
protected void encodeHeader(OutputStream os) throws IOException {
tokenHeader.encode(os);
}
/**
* Encodes a MessageToken_v2 onto an OutputStream.
*
* @param os the OutputStream to which this should be written
* @throws IOException is an error occurs while encoding the token
*/
public abstract void encode(OutputStream os) throws IOException;
protected final byte[] getTokenHeader() {
return (tokenHeader.getBytes());
}
// ******************************************* //
// I N N E R C L A S S E S F O L L O W
// ******************************************* //
/**
* This inner class represents the initial portion of the message token.
* It constitutes the first 16 bytes of the message token.
*/
class MessageTokenHeader {
private int tokenId;
private byte[] bytes = new byte[TOKEN_HEADER_SIZE];
// Writes a new token header
public MessageTokenHeader(int tokenId, boolean conf) throws GSSException {
this.tokenId = tokenId;
bytes[0] = (byte) (tokenId >>> 8);
bytes[1] = (byte) (tokenId);
// Flags (Note: MIT impl requires subkey)
int flags = 0;
flags = (initiator ? 0 : FLAG_SENDER_IS_ACCEPTOR) |
((conf && tokenId != MIC_ID_v2) ?
FLAG_WRAP_CONFIDENTIAL : 0) |
(have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0);
bytes[2] = (byte) flags;
// filler
bytes[3] = (byte) FILLER;
if (tokenId == WRAP_ID_v2) {
// EC field
bytes[4] = (byte) 0;
bytes[5] = (byte) 0;
// RRC field
bytes[6] = (byte) 0;
bytes[7] = (byte) 0;
} else if (tokenId == MIC_ID_v2) {
// more filler for MicToken
for (int i = 4; i < 8; i++) {
bytes[i] = (byte) FILLER;
}
}
// Calculate SND_SEQ, only write 4 bytes from the 12th position
writeBigEndian(seqNumber, bytes, 12);
}
/**
* Reads a MessageTokenHeader from an InputStream and sets the
* appropriate confidentiality and quality of protection
* values in a MessageProp structure.
*
* @param is the InputStream to read from
* @param prop the MessageProp to populate
* @throws IOException is an error occurs while reading from the
* InputStream
*/
public MessageTokenHeader(InputStream is, MessageProp prop, int tokId)
throws IOException, GSSException {
readFully(is, bytes, 0, TOKEN_HEADER_SIZE);
tokenId = readInt(bytes, TOKEN_ID_POS);
// validate Token ID
if (tokenId != tokId) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
getTokenName(tokenId) + ":" + "Defective Token ID!");
}
/*
* Validate new GSS TokenHeader
*/
// valid acceptor_flag
// If I am initiator, the received token should have ACCEPTOR on
int acceptor_flag = (initiator ? FLAG_SENDER_IS_ACCEPTOR : 0);
int flag = bytes[TOKEN_FLAG_POS] & FLAG_SENDER_IS_ACCEPTOR;
if (flag != acceptor_flag) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
getTokenName(tokenId) + ":" + "Acceptor Flag Error!");
}
// check for confidentiality
int conf_flag = bytes[TOKEN_FLAG_POS] & FLAG_WRAP_CONFIDENTIAL;
if ((conf_flag == FLAG_WRAP_CONFIDENTIAL) &&
(tokenId == WRAP_ID_v2)) {
prop.setPrivacy(true);
} else {
prop.setPrivacy(false);
}
if (tokenId == WRAP_ID_v2) {
// validate filler
if ((bytes[3] & 0xff) != FILLER) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
getTokenName(tokenId) + ":" + "Defective Token Filler!");
}
// read EC field
ec = readBigEndian(bytes, TOKEN_EC_POS, 2);
// read RRC field
rrc = readBigEndian(bytes, TOKEN_RRC_POS, 2);
} else if (tokenId == MIC_ID_v2) {
for (int i = 3; i < 8; i++) {
if ((bytes[i] & 0xff) != FILLER) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN,
-1, getTokenName(tokenId) + ":" +
"Defective Token Filler!");
}
}
}
// set default QOP
prop.setQOP(0);
// sequence number
seqNumber = readBigEndian(bytes, 0, 8);
}
/**
* Encodes this MessageTokenHeader onto an OutputStream
* @param os the OutputStream to write to
* @throws IOException is an error occurs while writing
*/
public final void encode(OutputStream os) throws IOException {
os.write(bytes);
}
/**
* Returns the token id for the message token.
* @return the token id
* @see sun.security.jgss.krb5.Krb5Token#MIC_ID_v2
* @see sun.security.jgss.krb5.Krb5Token#WRAP_ID_v2
*/
public final int getTokenId() {
return tokenId;
}
/**
* Returns the bytes of this header.
* @return 8 bytes that form this header
*/
public final byte[] getBytes() {
return bytes;
}
} // end of class MessageTokenHeader
}

View file

@ -0,0 +1,114 @@
/*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
class MicToken extends MessageToken {
public MicToken(Krb5Context context,
byte[] tokenBytes, int tokenOffset, int tokenLen,
MessageProp prop) throws GSSException {
super(Krb5Token.MIC_ID, context,
tokenBytes, tokenOffset, tokenLen, prop);
}
public MicToken(Krb5Context context,
InputStream is, MessageProp prop)
throws GSSException {
super(Krb5Token.MIC_ID, context, is, prop);
}
public void verify(byte[] data, int offset, int len) throws GSSException {
if (!verifySignAndSeqNumber(null, data, offset, len, null))
throw new GSSException(GSSException.BAD_MIC, -1,
"Corrupt checksum or sequence number in MIC token");
}
public void verify(InputStream data) throws GSSException {
byte[] dataBytes = null;
try {
dataBytes = new byte[data.available()];
data.read(dataBytes);
} catch (IOException e) {
// Error reading application data
throw new GSSException(GSSException.BAD_MIC, -1,
"Corrupt checksum or sequence number in MIC token");
}
verify(dataBytes, 0, dataBytes.length);
}
public MicToken(Krb5Context context, MessageProp prop,
byte[] data, int pos, int len)
throws GSSException {
super(Krb5Token.MIC_ID, context);
// debug("Application data to MicToken verify is [" +
// getHexBytes(data, pos, len) + "]\n");
if (prop == null) prop = new MessageProp(0, false);
genSignAndSeqNumber(prop, null, data, pos, len, null);
}
public MicToken(Krb5Context context, MessageProp prop,
InputStream data)
throws GSSException, IOException {
super(Krb5Token.MIC_ID, context);
byte[] dataBytes = new byte[data.available()];
data.read(dataBytes);
//debug("Application data to MicToken cons is [" +
// getHexBytes(dataBytes) + "]\n");
if (prop == null) prop = new MessageProp(0, false);
genSignAndSeqNumber(prop, null, dataBytes, 0, dataBytes.length, null);
}
protected int getSealAlg(boolean confRequested, int qop) {
return (SEAL_ALG_NONE);
}
public int encode(byte[] outToken, int offset)
throws IOException, GSSException {
// Token is small
ByteArrayOutputStream bos = new ByteArrayOutputStream();
super.encode(bos);
byte[] token = bos.toByteArray();
System.arraycopy(token, 0, outToken, offset, token.length);
return token.length;
}
public byte[] encode() throws IOException, GSSException{
// XXX Fine tune this initial size
ByteArrayOutputStream bos = new ByteArrayOutputStream(50);
encode(bos);
return bos.toByteArray();
}
}

View file

@ -0,0 +1,118 @@
/*
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
/**
* This class represents the new format of GSS MIC tokens, as specified
* in RFC 4121
*
* MIC tokens = { 16-byte token-header | HMAC }
* where HMAC is on { plaintext | 16-byte token-header }
*
* @author Seema Malkani
*/
class MicToken_v2 extends MessageToken_v2 {
public MicToken_v2(Krb5Context context,
byte[] tokenBytes, int tokenOffset, int tokenLen,
MessageProp prop) throws GSSException {
super(Krb5Token.MIC_ID_v2, context,
tokenBytes, tokenOffset, tokenLen, prop);
}
public MicToken_v2(Krb5Context context, InputStream is, MessageProp prop)
throws GSSException {
super(Krb5Token.MIC_ID_v2, context, is, prop);
}
public void verify(byte[] data, int offset, int len) throws GSSException {
if (!verifySign(data, offset, len))
throw new GSSException(GSSException.BAD_MIC, -1,
"Corrupt checksum or sequence number in MIC token");
}
public void verify(InputStream data) throws GSSException {
byte[] dataBytes = null;
try {
dataBytes = new byte[data.available()];
data.read(dataBytes);
} catch (IOException e) {
// Error reading application data
throw new GSSException(GSSException.BAD_MIC, -1,
"Corrupt checksum or sequence number in MIC token");
}
verify(dataBytes, 0, dataBytes.length);
}
public MicToken_v2(Krb5Context context, MessageProp prop,
byte[] data, int pos, int len)
throws GSSException {
super(Krb5Token.MIC_ID_v2, context);
// debug("Application data to MicToken verify is [" +
// getHexBytes(data, pos, len) + "]\n");
if (prop == null) prop = new MessageProp(0, false);
genSignAndSeqNumber(prop, data, pos, len);
}
public MicToken_v2(Krb5Context context, MessageProp prop, InputStream data)
throws GSSException, IOException {
super(Krb5Token.MIC_ID_v2, context);
byte[] dataBytes = new byte[data.available()];
data.read(dataBytes);
// debug("Application data to MicToken cons is [" +
// getHexBytes(dataBytes) + "]\n");
if (prop == null) prop = new MessageProp(0, false);
genSignAndSeqNumber(prop, dataBytes, 0, dataBytes.length);
}
public byte[] encode() throws IOException {
// XXX Fine tune this initial size
ByteArrayOutputStream bos = new ByteArrayOutputStream(50);
encode(bos);
return bos.toByteArray();
}
public int encode(byte[] outToken, int offset) throws IOException {
byte[] token = encode();
System.arraycopy(token, 0, outToken, offset, token.length);
return token.length;
}
public void encode(OutputStream os) throws IOException {
encodeHeader(os);
os.write(checksum);
}
}

View file

@ -0,0 +1,262 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KerberosKey;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KeyTab;
import javax.security.auth.Subject;
import sun.security.krb5.Credentials;
import sun.security.krb5.EncryptionKey;
import sun.security.krb5.KrbException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import sun.security.krb5.*;
import sun.security.krb5.internal.Krb5;
/**
* Credentials of a kerberos acceptor. A KerberosPrincipal object (kp) is
* the principal. It can be specified as the serverPrincipal argument
* in the getInstance() method, or uses only KerberosPrincipal in the subject.
* Otherwise, the creds object is unbound and kp is null.
*
* The class also encapsulates various secrets, which can be:
*
* 1. Some KerberosKeys (generated from password)
* 2. Some KeyTabs (for a typical service based on keytabs)
* 3. A TGT (for S4U2proxy extension or user2user)
*
* Note that some secrets can coexist. For example, a user2user service
* can use its keytab (or keys) if the client can successfully obtain a
* normal service ticket, or it can use the TGT (actually, the session key
* of the TGT) if the client can only acquire a service ticket
* of ENC-TKT-IN-SKEY style.
*
* @since 1.8
*/
public final class ServiceCreds {
// The principal, or null if unbound
private KerberosPrincipal kp;
// All principals in the subject's princ set
private Set<KerberosPrincipal> allPrincs;
// All private credentials that can be used
private List<KeyTab> ktabs;
private List<KerberosKey> kk;
private KerberosTicket tgt;
private boolean destroyed;
private ServiceCreds() {
// Make sure this class cannot be instantiated externally.
}
/**
* Creates a ServiceCreds object based on info in a Subject for
* a given principal name (if specified).
* @return the object, or null if there is no private creds for it
*/
public static ServiceCreds getInstance(
Subject subj, String serverPrincipal) {
ServiceCreds sc = new ServiceCreds();
sc.allPrincs =
subj.getPrincipals(KerberosPrincipal.class);
// Compatibility. A key implies its own principal
for (KerberosKey key: SubjectComber.findMany(
subj, serverPrincipal, null, KerberosKey.class)) {
sc.allPrincs.add(key.getPrincipal());
}
if (serverPrincipal != null) { // A named principal
sc.kp = new KerberosPrincipal(serverPrincipal);
} else {
// For compatibility reason, we set the name of default principal
// to the "only possible" name it can take, which means there is
// only one KerberosPrincipal and there is no unbound keytabs
if (sc.allPrincs.size() == 1) {
boolean hasUnbound = false;
for (KeyTab ktab: SubjectComber.findMany(
subj, null, null, KeyTab.class)) {
if (!ktab.isBound()) {
hasUnbound = true;
break;
}
}
if (!hasUnbound) {
sc.kp = sc.allPrincs.iterator().next();
serverPrincipal = sc.kp.getName();
}
}
}
sc.ktabs = SubjectComber.findMany(
subj, serverPrincipal, null, KeyTab.class);
sc.kk = SubjectComber.findMany(
subj, serverPrincipal, null, KerberosKey.class);
sc.tgt = SubjectComber.find(
subj, null, serverPrincipal, KerberosTicket.class);
if (sc.ktabs.isEmpty() && sc.kk.isEmpty() && sc.tgt == null) {
return null;
}
sc.destroyed = false;
return sc;
}
// can be null
public String getName() {
if (destroyed) {
throw new IllegalStateException("This object is destroyed");
}
return kp == null ? null : kp.getName();
}
/**
* Gets keys for "someone". Used in 2 cases:
* 1. By TLS because it needs to get keys before client comes in.
* 2. As a fallback in getEKeys() below.
* This method can still return an empty array.
*/
public KerberosKey[] getKKeys() {
if (destroyed) {
throw new IllegalStateException("This object is destroyed");
}
KerberosPrincipal one = kp; // named principal
if (one == null && !allPrincs.isEmpty()) { // or, a known principal
one = allPrincs.iterator().next();
}
if (one == null) { // Or, some random one
for (KeyTab ktab: ktabs) {
// Must be unbound keytab, otherwise, allPrincs is not empty
PrincipalName pn =
Krb5Util.snapshotFromJavaxKeyTab(ktab).getOneName();
if (pn != null) {
one = new KerberosPrincipal(pn.getName());
break;
}
}
}
if (one != null) {
return getKKeys(one);
} else {
return new KerberosKey[0];
}
}
/**
* Get kkeys for a principal,
* @param princ the target name initiator requests. Not null.
* @return keys for the princ, never null, might be empty
*/
public KerberosKey[] getKKeys(KerberosPrincipal princ) {
if (destroyed) {
throw new IllegalStateException("This object is destroyed");
}
ArrayList<KerberosKey> keys = new ArrayList<>();
if (kp != null && !princ.equals(kp)) { // named principal
return new KerberosKey[0];
}
for (KerberosKey k: kk) {
if (k.getPrincipal().equals(princ)) {
keys.add(k);
}
}
for (KeyTab ktab: ktabs) {
if (ktab.getPrincipal() == null && ktab.isBound()) {
// legacy bound keytab. although we don't know who
// the bound principal is, it must be in allPrincs
if (!allPrincs.contains(princ)) {
continue; // skip this legacy bound keytab
}
}
for (KerberosKey k: ktab.getKeys(princ)) {
keys.add(k);
}
}
return keys.toArray(new KerberosKey[keys.size()]);
}
/**
* Gets EKeys for a principal.
* @param princ the target name initiator requests. Not null.
* @return keys for the princ, never null, might be empty
*/
public EncryptionKey[] getEKeys(PrincipalName princ) {
if (destroyed) {
throw new IllegalStateException("This object is destroyed");
}
KerberosKey[] kkeys = getKKeys(new KerberosPrincipal(princ.getName()));
if (kkeys.length == 0) {
// Fallback: old JDK does not perform real name checking. If the
// acceptor has host.sun.com but initiator requests for host,
// as long as their keys match (i.e. keys for one can decrypt
// the other's service ticket), the authentication is OK.
// There are real customers depending on this to use different
// names for a single service.
kkeys = getKKeys();
}
EncryptionKey[] ekeys = new EncryptionKey[kkeys.length];
for (int i=0; i<ekeys.length; i++) {
ekeys[i] = new EncryptionKey(
kkeys[i].getEncoded(), kkeys[i].getKeyType(),
kkeys[i].getVersionNumber());
}
return ekeys;
}
public Credentials getInitCred() {
if (destroyed) {
throw new IllegalStateException("This object is destroyed");
}
if (tgt == null) {
return null;
}
try {
return Krb5Util.ticketToCreds(tgt);
} catch (KrbException | IOException e) {
return null;
}
}
public void destroy() {
// Do not wipe out real keys because they are references to the
// priv creds in subject. Just make it useless.
destroyed = true;
kp = null;
ktabs.clear();
kk.clear();
tgt = null;
}
}

View file

@ -0,0 +1,216 @@
/*
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KerberosKey;
import javax.security.auth.Subject;
import javax.security.auth.DestroyFailedException;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KeyTab;
/**
* This utility looks through the current Subject and retrieves private
* credentials for the desired client/server principals.
*
* @author Ram Marti
* @since 1.4.2
*/
class SubjectComber {
private static final boolean DEBUG = Krb5Util.DEBUG;
/**
* Default constructor
*/
private SubjectComber() { // Cannot create one of these
}
static <T> T find(Subject subject, String serverPrincipal,
String clientPrincipal, Class<T> credClass) {
// findAux returns T if oneOnly.
return credClass.cast(findAux(subject, serverPrincipal,
clientPrincipal, credClass, true));
}
@SuppressWarnings("unchecked") // findAux returns List<T> if !oneOnly.
static <T> List<T> findMany(Subject subject, String serverPrincipal,
String clientPrincipal, Class<T> credClass) {
return (List<T>)findAux(subject, serverPrincipal, clientPrincipal,
credClass, false);
}
/**
* Find private credentials for the specified client/server principals
* in the subject. Returns null if the subject is null.
*
* @return the private credentials
*/
// Returns T if oneOnly and List<T> if !oneOnly.
private static <T> Object findAux(Subject subject, String serverPrincipal,
String clientPrincipal, Class<T> credClass, boolean oneOnly) {
if (subject == null) {
return null;
} else {
List<T> answer = (oneOnly ? null : new ArrayList<T>());
if (credClass == KeyTab.class) {
Iterator<KeyTab> iterator =
subject.getPrivateCredentials(KeyTab.class).iterator();
while (iterator.hasNext()) {
KeyTab t = iterator.next();
if (serverPrincipal != null && t.isBound()) {
KerberosPrincipal name = t.getPrincipal();
if (name != null) {
if (!serverPrincipal.equals(name.getName())) {
continue;
}
} else {
// legacy bound keytab. although we don't know who
// the bound principal is, it must be in allPrincs
boolean found = false;
for (KerberosPrincipal princ:
subject.getPrincipals(KerberosPrincipal.class)) {
if (princ.getName().equals(serverPrincipal)) {
found = true;
break;
}
}
if (!found) continue;
}
}
// Check passed, we can add now
if (DEBUG) {
System.out.println("Found " + credClass.getSimpleName()
+ " " + t);
}
if (oneOnly) {
return t;
} else {
answer.add(credClass.cast(t));
}
}
} else if (credClass == KerberosKey.class) {
// We are looking for credentials for the serverPrincipal
Iterator<KerberosKey> iterator =
subject.getPrivateCredentials(KerberosKey.class).iterator();
while (iterator.hasNext()) {
KerberosKey t = iterator.next();
String name = t.getPrincipal().getName();
if (serverPrincipal == null || serverPrincipal.equals(name)) {
if (DEBUG) {
System.out.println("Found " +
credClass.getSimpleName() + " for " + name);
}
if (oneOnly) {
return t;
} else {
answer.add(credClass.cast(t));
}
}
}
} else if (credClass == KerberosTicket.class) {
// we are looking for a KerberosTicket credentials
// for client-service principal pair
Set<Object> pcs = subject.getPrivateCredentials();
synchronized (pcs) {
Iterator<Object> iterator = pcs.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
if (obj instanceof KerberosTicket) {
@SuppressWarnings("unchecked")
KerberosTicket ticket = (KerberosTicket)obj;
if (DEBUG) {
System.out.println("Found ticket for "
+ ticket.getClient()
+ " to go to "
+ ticket.getServer()
+ " expiring on "
+ ticket.getEndTime());
}
if (!ticket.isCurrent()) {
// let us remove the ticket from the Subject
// Note that both TGT and service ticket will be
// removed upon expiration
if (!subject.isReadOnly()) {
iterator.remove();
try {
ticket.destroy();
if (DEBUG) {
System.out.println("Removed and destroyed "
+ "the expired Ticket \n"
+ ticket);
}
} catch (DestroyFailedException dfe) {
if (DEBUG) {
System.out.println("Expired ticket not" +
" detroyed successfully. " + dfe);
}
}
}
} else {
if (serverPrincipal == null ||
ticket.getServer().getName().equals(serverPrincipal)) {
if (clientPrincipal == null ||
clientPrincipal.equals(
ticket.getClient().getName())) {
if (oneOnly) {
return ticket;
} else {
// Record names so that tickets will
// all belong to same principals
if (clientPrincipal == null) {
clientPrincipal =
ticket.getClient().getName();
}
if (serverPrincipal == null) {
serverPrincipal =
ticket.getServer().getName();
}
answer.add(credClass.cast(ticket));
}
}
}
}
}
}
}
}
return answer;
}
}
}

View file

@ -0,0 +1,532 @@
/*
* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.jgss.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import sun.security.krb5.Confounder;
/**
* This class represents a token emitted by the GSSContext.wrap()
* call. It is a MessageToken except that it also contains plaintext
* or encrypted data at the end. A wrapToken has certain other rules
* that are peculiar to it and different from a MICToken, which is
* another type of MessageToken. All data in a WrapToken is prepended
* by a random counfounder of 8 bytes. All data in a WrapToken is
* also padded with one to eight bytes where all bytes are equal in
* value to the number of bytes being padded. Thus, all application
* data is replaced by (confounder || data || padding).
*
* @author Mayank Upadhyay
*/
class WrapToken extends MessageToken {
/**
* The size of the random confounder used in a WrapToken.
*/
static final int CONFOUNDER_SIZE = 8;
/*
* The padding used with a WrapToken. All data is padded to the
* next multiple of 8 bytes, even if its length is already
* multiple of 8.
* Use this table as a quick way to obtain padding bytes by
* indexing it with the number of padding bytes required.
*/
static final byte[][] pads = {
null, // No, no one escapes padding
{0x01},
{0x02, 0x02},
{0x03, 0x03, 0x03},
{0x04, 0x04, 0x04, 0x04},
{0x05, 0x05, 0x05, 0x05, 0x05},
{0x06, 0x06, 0x06, 0x06, 0x06, 0x06},
{0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07},
{0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}
};
/*
* A token may come in either in an InputStream or as a
* byte[]. Store a reference to it in either case and process
* it's data only later when getData() is called and
* decryption/copying is needed to be done. Note that JCE can
* decrypt both from a byte[] and from an InputStream.
*/
private boolean readTokenFromInputStream = true;
private InputStream is = null;
private byte[] tokenBytes = null;
private int tokenOffset = 0;
private int tokenLen = 0;
/*
* Application data may come from an InputStream or from a
* byte[]. However, it will always be stored and processed as a
* byte[] since
* (a) the MessageDigest class only accepts a byte[] as input and
* (b) It allows writing to an OuputStream via a CipherOutputStream.
*/
private byte[] dataBytes = null;
private int dataOffset = 0;
private int dataLen = 0;
// the len of the token data: (confounder || data || padding)
private int dataSize = 0;
// Accessed by CipherHelper
byte[] confounder = null;
byte[] padding = null;
private boolean privacy = false;
/**
* Constructs a WrapToken from token bytes obtained from the
* peer.
* @param context the mechanism context associated with this
* token
* @param tokenBytes the bytes of the token
* @param tokenOffset the offset of the token
* @param tokenLen the length of the token
* @param prop the MessageProp into which characteristics of the
* parsed token will be stored.
* @throws GSSException if the token is defective
*/
public WrapToken(Krb5Context context,
byte[] tokenBytes, int tokenOffset, int tokenLen,
MessageProp prop) throws GSSException {
// Just parse the MessageToken part first
super(Krb5Token.WRAP_ID, context,
tokenBytes, tokenOffset, tokenLen, prop);
this.readTokenFromInputStream = false;
// Will need the token bytes again when extracting data
this.tokenBytes = tokenBytes;
this.tokenOffset = tokenOffset;
this.tokenLen = tokenLen;
this.privacy = prop.getPrivacy();
dataSize =
getGSSHeader().getMechTokenLength() - getKrb5TokenSize();
}
/**
* Constructs a WrapToken from token bytes read on the fly from
* an InputStream.
* @param context the mechanism context associated with this
* token
* @param is the InputStream containing the token bytes
* @param prop the MessageProp into which characteristics of the
* parsed token will be stored.
* @throws GSSException if the token is defective or if there is
* a problem reading from the InputStream
*/
public WrapToken(Krb5Context context,
InputStream is, MessageProp prop)
throws GSSException {
// Just parse the MessageToken part first
super(Krb5Token.WRAP_ID, context, is, prop);
// Will need the token bytes again when extracting data
this.is = is;
this.privacy = prop.getPrivacy();
/*
debug("WrapToken Cons: gssHeader.getMechTokenLength=" +
getGSSHeader().getMechTokenLength());
debug("\n token size="
+ getTokenSize());
*/
dataSize =
getGSSHeader().getMechTokenLength() - getTokenSize();
// debug("\n dataSize=" + dataSize);
// debug("\n");
}
/**
* Obtains the application data that was transmitted in this
* WrapToken.
* @return a byte array containing the application data
* @throws GSSException if an error occurs while decrypting any
* cipher text and checking for validity
*/
public byte[] getData() throws GSSException {
byte[] temp = new byte[dataSize];
getData(temp, 0);
// Remove the confounder and the padding
byte[] retVal = new byte[dataSize - confounder.length -
padding.length];
System.arraycopy(temp, 0, retVal, 0, retVal.length);
return retVal;
}
/**
* Obtains the application data that was transmitted in this
* WrapToken, writing it into an application provided output
* array.
* @param dataBuf the output buffer into which the data must be
* written
* @param dataBufOffset the offset at which to write the data
* @return the size of the data written
* @throws GSSException if an error occurs while decrypting any
* cipher text and checking for validity
*/
public int getData(byte[] dataBuf, int dataBufOffset)
throws GSSException {
if (readTokenFromInputStream)
getDataFromStream(dataBuf, dataBufOffset);
else
getDataFromBuffer(dataBuf, dataBufOffset);
return (dataSize - confounder.length - padding.length);
}
/**
* Helper routine to obtain the application data transmitted in
* this WrapToken. It is called if the WrapToken was constructed
* with a byte array as input.
* @param dataBuf the output buffer into which the data must be
* written
* @param dataBufOffset the offset at which to write the data
* @throws GSSException if an error occurs while decrypting any
* cipher text and checking for validity
*/
private void getDataFromBuffer(byte[] dataBuf, int dataBufOffset)
throws GSSException {
GSSHeader gssHeader = getGSSHeader();
int dataPos = tokenOffset +
gssHeader.getLength() + getTokenSize();
if (dataPos + dataSize > tokenOffset + tokenLen)
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"Insufficient data in "
+ getTokenName(getTokenId()));
// debug("WrapToken cons: data is token is [" +
// getHexBytes(tokenBytes, tokenOffset, tokenLen) + "]\n");
confounder = new byte[CONFOUNDER_SIZE];
// Do decryption if this token was privacy protected.
if (privacy) {
cipherHelper.decryptData(this,
tokenBytes, dataPos, dataSize, dataBuf, dataBufOffset);
/*
debug("\t\tDecrypted data is [" +
getHexBytes(confounder) + " " +
getHexBytes(dataBuf, dataBufOffset,
dataSize - CONFOUNDER_SIZE - padding.length) +
getHexBytes(padding) +
"]\n");
*/
} else {
// Token data is in cleartext
// debug("\t\tNo encryption was performed by peer.\n");
System.arraycopy(tokenBytes, dataPos,
confounder, 0, CONFOUNDER_SIZE);
int padSize = tokenBytes[dataPos + dataSize - 1];
if (padSize < 0)
padSize = 0;
if (padSize > 8)
padSize %= 8;
padding = pads[padSize];
// debug("\t\tPadding applied was: " + padSize + "\n");
System.arraycopy(tokenBytes, dataPos + CONFOUNDER_SIZE,
dataBuf, dataBufOffset, dataSize -
CONFOUNDER_SIZE - padSize);
// byte[] debugbuf = new byte[dataSize - CONFOUNDER_SIZE - padSize];
// System.arraycopy(tokenBytes, dataPos + CONFOUNDER_SIZE,
// debugbuf, 0, debugbuf.length);
// debug("\t\tData is: " + getHexBytes(debugbuf, debugbuf.length));
}
/*
* Make sure sign and sequence number are not corrupt
*/
if (!verifySignAndSeqNumber(confounder,
dataBuf, dataBufOffset,
dataSize - CONFOUNDER_SIZE
- padding.length,
padding))
throw new GSSException(GSSException.BAD_MIC, -1,
"Corrupt checksum or sequence number in Wrap token");
}
/**
* Helper routine to obtain the application data transmitted in
* this WrapToken. It is called if the WrapToken was constructed
* with an Inputstream.
* @param dataBuf the output buffer into which the data must be
* written
* @param dataBufOffset the offset at which to write the data
* @throws GSSException if an error occurs while decrypting any
* cipher text and checking for validity
*/
private void getDataFromStream(byte[] dataBuf, int dataBufOffset)
throws GSSException {
GSSHeader gssHeader = getGSSHeader();
// Don't check the token length. Data will be read on demand from
// the InputStream.
// debug("WrapToken cons: data will be read from InputStream.\n");
confounder = new byte[CONFOUNDER_SIZE];
try {
// Do decryption if this token was privacy protected.
if (privacy) {
cipherHelper.decryptData(this, is, dataSize,
dataBuf, dataBufOffset);
// debug("\t\tDecrypted data is [" +
// getHexBytes(confounder) + " " +
// getHexBytes(dataBuf, dataBufOffset,
// dataSize - CONFOUNDER_SIZE - padding.length) +
// getHexBytes(padding) +
// "]\n");
} else {
// Token data is in cleartext
// debug("\t\tNo encryption was performed by peer.\n");
readFully(is, confounder);
if (cipherHelper.isArcFour()) {
padding = pads[1];
readFully(is, dataBuf, dataBufOffset, dataSize-CONFOUNDER_SIZE-1);
} else {
// Data is always a multiple of 8 with this GSS Mech
// Copy all but last block as they are
int numBlocks = (dataSize - CONFOUNDER_SIZE)/8 - 1;
int offset = dataBufOffset;
for (int i = 0; i < numBlocks; i++) {
readFully(is, dataBuf, offset, 8);
offset += 8;
}
byte[] finalBlock = new byte[8];
readFully(is, finalBlock);
int padSize = finalBlock[7];
padding = pads[padSize];
// debug("\t\tPadding applied was: " + padSize + "\n");
System.arraycopy(finalBlock, 0, dataBuf, offset,
finalBlock.length - padSize);
}
}
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
getTokenName(getTokenId())
+ ": " + e.getMessage());
}
/*
* Make sure sign and sequence number are not corrupt
*/
if (!verifySignAndSeqNumber(confounder,
dataBuf, dataBufOffset,
dataSize - CONFOUNDER_SIZE
- padding.length,
padding))
throw new GSSException(GSSException.BAD_MIC, -1,
"Corrupt checksum or sequence number in Wrap token");
}
/**
* Helper routine to pick the right padding for a certain length
* of application data. Every application message has some
* padding between 1 and 8 bytes.
* @param len the length of the application data
* @return the padding to be applied
*/
private byte[] getPadding(int len) {
int padSize = 0;
// For RC4-HMAC, all padding is rounded up to 1 byte.
// One byte is needed to say that there is 1 byte of padding.
if (cipherHelper.isArcFour()) {
padSize = 1;
} else {
padSize = len % 8;
padSize = 8 - padSize;
}
return pads[padSize];
}
public WrapToken(Krb5Context context, MessageProp prop,
byte[] dataBytes, int dataOffset, int dataLen)
throws GSSException {
super(Krb5Token.WRAP_ID, context);
confounder = Confounder.bytes(CONFOUNDER_SIZE);
padding = getPadding(dataLen);
dataSize = confounder.length + dataLen + padding.length;
this.dataBytes = dataBytes;
this.dataOffset = dataOffset;
this.dataLen = dataLen;
/*
debug("\nWrapToken cons: data to wrap is [" +
getHexBytes(confounder) + " " +
getHexBytes(dataBytes, dataOffset, dataLen) + " " +
// padding is never null for Wrap
getHexBytes(padding) + "]\n");
*/
genSignAndSeqNumber(prop,
confounder,
dataBytes, dataOffset, dataLen,
padding);
/*
* If the application decides to ask for privacy when the context
* did not negotiate for it, do not provide it. The peer might not
* have support for it. The app will realize this with a call to
* pop.getPrivacy() after wrap().
*/
if (!context.getConfState())
prop.setPrivacy(false);
privacy = prop.getPrivacy();
}
public void encode(OutputStream os) throws IOException, GSSException {
super.encode(os);
// debug("Writing data: [");
if (!privacy) {
// debug(getHexBytes(confounder, confounder.length));
os.write(confounder);
// debug(" " + getHexBytes(dataBytes, dataOffset, dataLen));
os.write(dataBytes, dataOffset, dataLen);
// debug(" " + getHexBytes(padding, padding.length));
os.write(padding);
} else {
cipherHelper.encryptData(this, confounder,
dataBytes, dataOffset, dataLen, padding, os);
}
// debug("]\n");
}
public byte[] encode() throws IOException, GSSException {
// XXX Fine tune this initial size
ByteArrayOutputStream bos = new ByteArrayOutputStream(dataSize + 50);
encode(bos);
return bos.toByteArray();
}
public int encode(byte[] outToken, int offset)
throws IOException, GSSException {
// Token header is small
ByteArrayOutputStream bos = new ByteArrayOutputStream();
super.encode(bos);
byte[] header = bos.toByteArray();
System.arraycopy(header, 0, outToken, offset, header.length);
offset += header.length;
// debug("WrapToken.encode: Writing data: [");
if (!privacy) {
// debug(getHexBytes(confounder, confounder.length));
System.arraycopy(confounder, 0, outToken, offset,
confounder.length);
offset += confounder.length;
// debug(" " + getHexBytes(dataBytes, dataOffset, dataLen));
System.arraycopy(dataBytes, dataOffset, outToken, offset,
dataLen);
offset += dataLen;
// debug(" " + getHexBytes(padding, padding.length));
System.arraycopy(padding, 0, outToken, offset, padding.length);
} else {
cipherHelper.encryptData(this, confounder, dataBytes,
dataOffset, dataLen, padding, outToken, offset);
// debug(getHexBytes(outToken, offset, dataSize));
}
// debug("]\n");
// %%% assume that plaintext length == ciphertext len
return (header.length + confounder.length + dataLen + padding.length);
}
protected int getKrb5TokenSize() throws GSSException {
return (getTokenSize() + dataSize);
}
protected int getSealAlg(boolean conf, int qop) throws GSSException {
if (!conf) {
return SEAL_ALG_NONE;
}
// ignore QOP
return cipherHelper.getSealAlg();
}
// This implementation is way too conservative. And it certainly
// doesn't return the maximum limit.
static int getSizeLimit(int qop, boolean confReq, int maxTokenSize,
CipherHelper ch) throws GSSException {
return (GSSHeader.getMaxMechTokenSize(OID, maxTokenSize) -
(getTokenSize(ch) + CONFOUNDER_SIZE) - 8); /* safety */
}
}

View file

@ -0,0 +1,231 @@
/*
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.jgss.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
import sun.security.krb5.Confounder;
/**
* This class represents the new format of GSS tokens, as specified in RFC
* 4121, emitted by the GSSContext.wrap() call. It is a MessageToken except
* that it also contains plaintext or encrypted data at the end. A WrapToken
* has certain other rules that are peculiar to it and different from a
* MICToken, which is another type of MessageToken. All data in a WrapToken is
* prepended by a random confounder of 16 bytes. Thus, all application data
* is replaced by (confounder || data || tokenHeader || checksum).
*
* @author Seema Malkani
*/
class WrapToken_v2 extends MessageToken_v2 {
// Accessed by CipherHelper
byte[] confounder = null;
private final boolean privacy;
/**
* Constructs a WrapToken from token bytes obtained from the
* peer.
* @param context the mechanism context associated with this
* token
* @param tokenBytes the bytes of the token
* @param tokenOffset the offset of the token
* @param tokenLen the length of the token
* @param prop the MessageProp into which characteristics of the
* parsed token will be stored.
* @throws GSSException if the token is defective
*/
public WrapToken_v2(Krb5Context context,
byte[] tokenBytes, int tokenOffset, int tokenLen,
MessageProp prop) throws GSSException {
super(Krb5Token.WRAP_ID_v2, context,
tokenBytes, tokenOffset, tokenLen, prop);
this.privacy = prop.getPrivacy();
}
/**
* Constructs a WrapToken from token bytes read on the fly from
* an InputStream.
* @param context the mechanism context associated with this
* token
* @param is the InputStream containing the token bytes
* @param prop the MessageProp into which characteristics of the
* parsed token will be stored.
* @throws GSSException if the token is defective or if there is
* a problem reading from the InputStream
*/
public WrapToken_v2(Krb5Context context,
InputStream is, MessageProp prop)
throws GSSException {
super(Krb5Token.WRAP_ID_v2, context, is, prop);
this.privacy = prop.getPrivacy();
}
/**
* Obtains the application data that was transmitted in this
* WrapToken.
* @return a byte array containing the application data
* @throws GSSException if an error occurs while decrypting any
* cipher text and checking for validity
*/
public byte[] getData() throws GSSException {
byte[] temp = new byte[tokenDataLen];
int len = getData(temp, 0);
return Arrays.copyOf(temp, len);
}
/**
* Obtains the application data that was transmitted in this
* WrapToken, writing it into an application provided output
* array.
* @param dataBuf the output buffer into which the data must be
* written
* @param dataBufOffset the offset at which to write the data
* @return the size of the data written
* @throws GSSException if an error occurs while decrypting any
* cipher text and checking for validity
*/
public int getData(byte[] dataBuf, int dataBufOffset)
throws GSSException {
// debug("WrapToken cons: data is token is [" +
// getHexBytes(tokenBytes, tokenOffset, tokenLen) + "]\n");
// Do decryption if this token was privacy protected.
if (privacy) {
// decrypt data
cipherHelper.decryptData(this, tokenData, 0, tokenDataLen,
dataBuf, dataBufOffset, getKeyUsage());
return tokenDataLen - CONFOUNDER_SIZE -
TOKEN_HEADER_SIZE - cipherHelper.getChecksumLength();
} else {
// Token data is in cleartext
// debug("\t\tNo encryption was performed by peer.\n");
// data
int data_length = tokenDataLen - cipherHelper.getChecksumLength();
System.arraycopy(tokenData, 0,
dataBuf, dataBufOffset,
data_length);
// debug("\t\tData is: " + getHexBytes(dataBuf, data_length));
/*
* Make sure checksum is not corrupt
*/
if (!verifySign(dataBuf, dataBufOffset, data_length)) {
throw new GSSException(GSSException.BAD_MIC, -1,
"Corrupt checksum in Wrap token");
}
return data_length;
}
}
/**
* Writes a WrapToken_v2 object
*/
public WrapToken_v2(Krb5Context context, MessageProp prop,
byte[] dataBytes, int dataOffset, int dataLen)
throws GSSException {
super(Krb5Token.WRAP_ID_v2, context);
confounder = Confounder.bytes(CONFOUNDER_SIZE);
// debug("\nWrapToken cons: data to wrap is [" +
// getHexBytes(confounder) + " " +
// getHexBytes(dataBytes, dataOffset, dataLen) + "]\n");
genSignAndSeqNumber(prop, dataBytes, dataOffset, dataLen);
/*
* If the application decides to ask for privacy when the context
* did not negotiate for it, do not provide it. The peer might not
* have support for it. The app will realize this with a call to
* pop.getPrivacy() after wrap().
*/
if (!context.getConfState())
prop.setPrivacy(false);
privacy = prop.getPrivacy();
if (!privacy) {
// Wrap Tokens (without confidentiality) =
// { 16 byte token_header | plaintext | 12-byte HMAC }
// where HMAC is on { plaintext | token_header }
tokenData = new byte[dataLen + checksum.length];
System.arraycopy(dataBytes, dataOffset, tokenData, 0, dataLen);
System.arraycopy(checksum, 0, tokenData, dataLen, checksum.length);
} else {
// Wrap Tokens (with confidentiality) =
// { 16 byte token_header |
// Encrypt(16-byte confounder | plaintext | token_header) |
// 12-byte HMAC }
tokenData = cipherHelper.encryptData(this, confounder, getTokenHeader(),
dataBytes, dataOffset, dataLen, getKeyUsage());
}
}
public void encode(OutputStream os) throws IOException {
encodeHeader(os);
os.write(tokenData);
}
public byte[] encode() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream(
MessageToken_v2.TOKEN_HEADER_SIZE + tokenData.length);
encode(bos);
return bos.toByteArray();
}
public int encode(byte[] outToken, int offset) throws IOException {
byte[] token = encode();
System.arraycopy(token, 0, outToken, offset, token.length);
return token.length;
}
// This implementation is way to conservative. And it certainly
// doesn't return the maximum limit.
static int getSizeLimit(int qop, boolean confReq, int maxTokenSize,
CipherHelper ch) throws GSSException {
return (GSSHeader.getMaxMechTokenSize(OID, maxTokenSize) -
(TOKEN_HEADER_SIZE + ch.getChecksumLength() + CONFOUNDER_SIZE)
- 8 /* safety */);
}
}

View file

@ -0,0 +1,407 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.jgss.spi;
import org.ietf.jgss.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Provider;
/**
* This interface is implemented by a mechanism specific instance of a GSS
* security context.
* A GSSContextSpi object can be thought of having 3 states:
* -before initialization
* -during initialization with its peer
* -after it is established
* <p>
* The context options can only be requested in state 1. In state 3,
* the per message operations are available to the callers. The get
* methods for the context options will return the requested options
* while in state 1 and 2, and the established values in state 3.
* Some mechanisms may allow the access to the per-message operations
* and the context flags before the context is fully established. The
* isProtReady method is used to indicate that these services are
* available.
* <p>
* <strong>
* Context establishment tokens are defined in a mechanism independent
* format in section 3.1 of RFC 2743. The GSS-Framework will add
* and remove the mechanism independent header portion of this token format
* depending on whether a token is received or is being sent. The mechanism
* should only generate or expect to read the inner-context token portion.
* <br>
* On the other hands, tokens used for per-message calls are generated
* entirely by the mechanism. It is possible that the mechanism chooses to
* encase inner-level per-message tokens in a header similar to that used
* for initial tokens, however, this is upto the mechanism to do. The token
* to/from the per-message calls are opaque to the GSS-Framework.
* </strong>
* <p>
* An attempt has been made to allow for reading the peer's tokens from an
* InputStream and writing tokens for the peer to an OutputStream. This
* allows applications to pass in streams that are obtained from their network
* connections and thus minimize the buffer copies that will happen. This
* is especially important for tokens generated by wrap() which are
* proportional in size to the length of the application data being
* wrapped, and are probably also the most frequently used type of tokens.
* <p>
* It is anticipated that most applications will want to use wrap() in a
* fashion where they obtain the application bytes to wrap from a byte[]
* but want to output the wrap token straight to an
* OutputStream. Similarly, they will want to use unwrap() where they read
* the token directly form an InputStream but output it to some byte[] for
* the application to process. Unfortunately the high level GSS bindings
* do not contain overloaded forms of wrap() and unwrap() that do just
* this, however we have accomodated those cases here with the expectation
* that this will be rolled into the high level bindings sooner or later.
*
* @author Mayank Upadhyay
*/
public interface GSSContextSpi {
public Provider getProvider();
// The specification for the following methods mirrors the
// specification of the same methods in the GSSContext interface, as
// defined in RFC 2853.
public void requestLifetime(int lifetime) throws GSSException;
public void requestMutualAuth(boolean state) throws GSSException;
public void requestReplayDet(boolean state) throws GSSException;
public void requestSequenceDet(boolean state) throws GSSException;
public void requestCredDeleg(boolean state) throws GSSException;
public void requestAnonymity(boolean state) throws GSSException;
public void requestConf(boolean state) throws GSSException;
public void requestInteg(boolean state) throws GSSException;
public void requestDelegPolicy(boolean state) throws GSSException;
public void setChannelBinding(ChannelBinding cb) throws GSSException;
public boolean getCredDelegState();
public boolean getMutualAuthState();
public boolean getReplayDetState();
public boolean getSequenceDetState();
public boolean getAnonymityState();
public boolean getDelegPolicyState();
public boolean isTransferable() throws GSSException;
public boolean isProtReady();
public boolean isInitiator();
public boolean getConfState();
public boolean getIntegState();
public int getLifetime();
public boolean isEstablished();
public GSSNameSpi getSrcName() throws GSSException;
public GSSNameSpi getTargName() throws GSSException;
public Oid getMech() throws GSSException;
public GSSCredentialSpi getDelegCred() throws GSSException;
/**
* Initiator context establishment call. This method may be
* required to be called several times. A CONTINUE_NEEDED return
* call indicates that more calls are needed after the next token
* is received from the peer.
* <p>
* This method is called by the GSS-Framework when the application
* calls the initSecContext method on the GSSContext implementation
* that it has a reference to.
* <p>
* All overloaded forms of GSSContext.initSecContext() can be handled
* with this mechanism level initSecContext. Since the output token
* from this method is a fixed size, not exeedingly large, and a one
* time deal, an overloaded form that takes an OutputStream has not
* been defined. The GSS-Framwork can write the returned byte[] to any
* application provided OutputStream. Similarly, any application input
* int he form of byte arrays will be wrapped in an input stream by the
* GSS-Framework and then passed here.
* <p>
* <strong>
* The GSS-Framework will strip off the leading mechanism independent
* GSS-API header. In other words, only the mechanism specific
* inner-context token of RFC 2743 section 3.1 will be available on the
* InputStream.
* </strong>
*
* @param is contains the inner context token portion of the GSS token
* received from the peer. On the first call to initSecContext, there
* will be no token hence it will be ignored.
* @param mechTokenSize the size of the inner context token as read by
* the GSS-Framework from the mechanism independent GSS-API level
* header.
* @return any inner-context token required to be sent to the peer as
* part of a GSS token. The mechanism should not add the mechanism
* independent part of the token. The GSS-Framework will add that on
* the way out.
* @exception GSSException may be thrown
*/
public byte[] initSecContext(InputStream is, int mechTokenSize)
throws GSSException;
/**
* Acceptor's context establishment call. This method may be
* required to be called several times. A CONTINUE_NEEDED return
* call indicates that more calls are needed after the next token
* is received from the peer.
* <p>
* This method is called by the GSS-Framework when the application
* calls the acceptSecContext method on the GSSContext implementation
* that it has a reference to.
* <p>
* All overloaded forms of GSSContext.acceptSecContext() can be handled
* with this mechanism level acceptSecContext. Since the output token
* from this method is a fixed size, not exeedingly large, and a one
* time deal, an overloaded form that takes an OutputStream has not
* been defined. The GSS-Framwork can write the returned byte[] to any
* application provided OutputStream. Similarly, any application input
* int he form of byte arrays will be wrapped in an input stream by the
* GSS-Framework and then passed here.
* <p>
* <strong>
* The GSS-Framework will strip off the leading mechanism independent
* GSS-API header. In other words, only the mechanism specific
* inner-context token of RFC 2743 section 3.1 will be available on the
* InputStream.
* </strong>
*
* @param is contains the inner context token portion of the GSS token
* received from the peer.
* @param mechTokenSize the size of the inner context token as read by
* the GSS-Framework from the mechanism independent GSS-API level
* header.
* @return any inner-context token required to be sent to the peer as
* part of a GSS token. The mechanism should not add the mechanism
* independent part of the token. The GSS-Framework will add that on
* the way out.
* @exception GSSException may be thrown
*/
public byte[] acceptSecContext(InputStream is, int mechTokenSize)
throws GSSException;
/**
* Queries the context for largest data size to accommodate
* the specified protection and for the token to remain less then
* maxTokSize.
*
* @param qop the quality of protection that the context will be
* asked to provide.
* @param confReq a flag indicating whether confidentiality will be
* requested or not
* @param maxTokSize the maximum size of the output token
* @return the maximum size for the input message that can be
* provided to the wrap() method in order to guarantee that these
* requirements are met.
* @exception GSSException may be thrown
*/
public int getWrapSizeLimit(int qop, boolean confReq, int maxTokSize)
throws GSSException;
/**
* Provides per-message token encapsulation.
*
* @param is the user-provided message to be protected
* @param os the token to be sent to the peer. It includes
* the message from <i>is</i> with the requested protection.
* @param msgProp on input it contains the requested qop and
* confidentiality state, on output, the applied values
* @exception GSSException may be thrown
* @see unwrap
*/
public void wrap(InputStream is, OutputStream os, MessageProp msgProp)
throws GSSException;
/**
* For apps that want simplicity and don't care about buffer copies.
*/
public byte[] wrap(byte[] inBuf, int offset, int len,
MessageProp msgProp) throws GSSException;
/**
* For apps that care about buffer copies but either cannot use streams
* or want to avoid them for whatever reason. (Say, they are using
* block ciphers.)
*
* NOTE: This method is not defined in public class org.ietf.jgss.GSSContext
*
public int wrap(byte[] inBuf, int inOffset, int len,
byte[] outBuf, int outOffset,
MessageProp msgProp) throws GSSException;
*/
/**
* For apps that want to read from a specific application provided
* buffer but want to write directly to the network stream.
*/
/*
* Can be achieved by converting the input buffer to a
* ByteInputStream. Provided to keep the API consistent
* with unwrap.
*
* NOTE: This method is not defined in public class org.ietf.jgss.GSSContext
*
public void wrap(byte[] inBuf, int offset, int len,
OutputStream os, MessageProp msgProp)
throws GSSException;
*/
/**
* Retrieves the message token previously encapsulated in the wrap
* call.
*
* @param is the token from the peer
* @param os unprotected message data
* @param msgProp will contain the applied qop and confidentiality
* of the input token and any informatory status values
* @exception GSSException may be thrown
* @see wrap
*/
public void unwrap(InputStream is, OutputStream os,
MessageProp msgProp) throws GSSException;
/**
* For apps that want simplicity and don't care about buffer copies.
*/
public byte[] unwrap(byte[] inBuf, int offset, int len,
MessageProp msgProp) throws GSSException;
/**
* For apps that care about buffer copies but either cannot use streams
* or want to avoid them for whatever reason. (Say, they are using
* block ciphers.)
*
* NOTE: This method is not defined in public class org.ietf.jgss.GSSContext
*
public int unwrap(byte[] inBuf, int inOffset, int len,
byte[] outBuf, int outOffset,
MessageProp msgProp) throws GSSException;
*/
/**
* For apps that care about buffer copies and want to read
* straight from the network, but also want the output in a specific
* application provided buffer, say to reduce buffer allocation or
* subsequent copy.
*
* NOTE: This method is not defined in public class org.ietf.jgss.GSSContext
*
public int unwrap(InputStream is,
byte[] outBuf, int outOffset,
MessageProp msgProp) throws GSSException;
*/
/**
* Applies per-message integrity services.
*
* @param is the user-provided message
* @param os the token to be sent to the peer along with the
* message token. The message token <b>is not</b> encapsulated.
* @param msgProp on input the desired QOP and output the applied QOP
* @exception GSSException
*/
public void getMIC(InputStream is, OutputStream os,
MessageProp msgProp)
throws GSSException;
public byte[] getMIC(byte[] inMsg, int offset, int len,
MessageProp msgProp) throws GSSException;
/**
* Checks the integrity of the supplied tokens.
* This token was previously generated by getMIC.
*
* @param is token generated by getMIC
* @param msgStr the message to check integrity for
* @param mProp will contain the applied QOP and confidentiality
* states of the token as well as any informatory status codes
* @exception GSSException may be thrown
*/
public void verifyMIC(InputStream is, InputStream msgStr,
MessageProp mProp) throws GSSException;
public void verifyMIC(byte[] inTok, int tokOffset, int tokLen,
byte[] inMsg, int msgOffset, int msgLen,
MessageProp msgProp) throws GSSException;
/**
* Produces a token representing this context. After this call
* the context will no longer be usable until an import is
* performed on the returned token.
*
* @return exported context token
* @exception GSSException may be thrown
*/
public byte[] export() throws GSSException;
/**
* Releases context resources and terminates the
* context between 2 peer.
*
* @exception GSSException may be thrown
*/
public void dispose() throws GSSException;
/**
* Return the mechanism-specific attribute associated with {@code type}.
*
* @param type the type of the attribute requested
* @return the attribute
* @throws GSSException see {@link ExtendedGSSContext#inquireSecContext}
* for details
*/
public Object inquireSecContext(String type)
throws GSSException;
}

View file

@ -0,0 +1,108 @@
/*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spi;
import org.ietf.jgss.*;
import java.security.Provider;
/**
* This interface is implemented by a mechanism specific credential
* element. A GSSCredential is conceptually a container class of several
* credential elements from different mechanisms.
*
* @author Mayank Upadhyay
*/
public interface GSSCredentialSpi {
public Provider getProvider();
/**
* Called to invalidate this credential element and release
* any system recourses and cryptographic information owned
* by the credential.
*
* @exception GSSException with major codes NO_CRED and FAILURE
*/
public void dispose() throws GSSException;
/**
* Returns the principal name for this credential. The name
* is in mechanism specific format.
*
* @return GSSNameSpi representing principal name of this credential
* @exception GSSException may be thrown
*/
public GSSNameSpi getName() throws GSSException;
/**
* Returns the init lifetime remaining.
*
* @return the init lifetime remaining in seconds
* @exception GSSException may be thrown
*/
public int getInitLifetime() throws GSSException;
/**
* Returns the accept lifetime remaining.
*
* @return the accept lifetime remaining in seconds
* @exception GSSException may be thrown
*/
public int getAcceptLifetime() throws GSSException;
/**
* Determines if this credential element can be used by a context
* initiator.
* @return true if it can be used for initiating contexts
*/
public boolean isInitiatorCredential() throws GSSException;
/**
* Determines if this credential element can be used by a context
* acceptor.
* @return true if it can be used for accepting contexts
*/
public boolean isAcceptorCredential() throws GSSException;
/**
* Returns the oid representing the underlying credential
* mechanism oid.
*
* @return the Oid for this credential mechanism
* @exception GSSException may be thrown
*/
public Oid getMechanism();
/**
* Impersonates another client.
*
* @param name the client to impersonate
* @return the new credential
* @exception GSSException may be thrown
*/
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException;
}

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spi;
import org.ietf.jgss.*;
import java.security.Provider;
/**
* This interface is implemented by a mechanism specific name element. A
* GSSName is conceptually a container class of several name elements from
* different mechanisms.
*
* @author Mayank Upadhyay
*/
public interface GSSNameSpi {
public Provider getProvider();
/**
* Equals method for the GSSNameSpi objects.
* If either name denotes an anonymous principal, the call should
* return false.
*
* @param name to be compared with
* @return true if they both refer to the same entity, else false
* @exception GSSException with major codes of BAD_NAMETYPE,
* BAD_NAME, FAILURE
*/
public boolean equals(GSSNameSpi name) throws GSSException;
/**
* Compares this <code>GSSNameSpi</code> object to another Object
* that might be a <code>GSSNameSpi</code>. The behaviour is exactly
* the same as in {@link #equals(GSSNameSpi) equals} except that
* no GSSException is thrown; instead, false will be returned in the
* situation where an error occurs.
*
* @param another the object to be compared to
* @return true if they both refer to the same entity, else false
* @see #equals(GSSNameSpi)
*/
public boolean equals(Object another);
/**
* Returns a hashcode value for this GSSNameSpi.
*
* @return a hashCode value
*/
public int hashCode();
/**
* Returns a flat name representation for this object. The name
* format is defined in RFC 2078.
*
* @return the flat name representation for this object
* @exception GSSException with major codes NAME_NOT_MN, BAD_NAME,
* BAD_NAME, FAILURE.
*/
public byte[] export() throws GSSException;
/**
* Get the mechanism type that this NameElement corresponds to.
*
* @return the Oid of the mechanism type
*/
public Oid getMechanism();
/**
* Returns a string representation for this name. The printed
* name type can be obtained by calling getStringNameType().
*
* @return string form of this name
* @see #getStringNameType()
* @overrides Object#toString
*/
public String toString();
/**
* Returns the oid describing the format of the printable name.
*
* @return the Oid for the format of the printed name
*/
public Oid getStringNameType();
/**
* Indicates if this name object represents an Anonymous name.
*/
public boolean isAnonymousName();
}

View file

@ -0,0 +1,214 @@
/*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spi;
import org.ietf.jgss.*;
import java.security.Provider;
/**
* This interface is implemented by the factory class for every
* plugin mechanism. The GSSManager locates an implementation of this
* interface by querying the security providers installed on the
* system. For a provider to support a mechanism defined by Oid x.y.z,
* the provider master file would have to contain a mapping from the
* property "GssApiMechanism.x.y.z" to an implementation class that serves
* as the factory for that mechanism.
* <p>
* e.g., If a provider master file contained the a mapping from the
* property "GssApiMechanism.1.2.840.113554.1.2.2" to the class name
* "com.foo.krb5.Krb5GssFactory", then the GSS-API framework would assume
* that com.foo.krb5.Krb5GssFactory implements the MechanismFactory
* interface and that it can be used to obtain elements required by for
* supporting this mechanism.
*
* @author Mayank Upadhyay
*/
public interface MechanismFactory {
/**
* Returns the Oid of the mechanism that this factory supports.
* @return the Oid
*/
public Oid getMechanismOid();
/**
* Returns the provider that this factory came from.
* @return the provider
*/
public Provider getProvider();
/**
* Returns the GSS-API nametypes that this mechanism can
* support. Having this method helps the GSS-Framework decide quickly
* if a certain mechanism can be skipped when importing a name.
* @return an array of the Oid's corresponding to the different GSS-API
* nametypes supported
* @see org.ietf.jgss.GSSName
*/
public Oid[] getNameTypes() throws GSSException;
/**
* Creates a credential element for this mechanism to be included as
* part of a GSSCredential implementation. A GSSCredential is
* conceptually a container class of several credential elements from
* different mechanisms. A GSS-API credential can be used either for
* initiating GSS security contexts or for accepting them. This method
* also accepts parameters that indicate what usage is expected and how
* long the life of the credential should be. It is not necessary that
* the mechanism honor the request for lifetime. An application will
* always query an acquired GSSCredential to determine what lifetime it
* got back.<p>
*
* <b>Not all mechanisms support the concept of one credential element
* that can be used for both initiating and accepting a context. In the
* event that an application requests usage INITIATE_AND_ACCEPT for a
* credential from such a mechanism, the GSS framework will need to
* obtain two different credential elements from the mechanism, one
* that will have usage INITIATE_ONLY and another that will have usage
* ACCEPT_ONLY. The mechanism will help the GSS-API realize this by
* returning a credential element with usage INITIATE_ONLY or
* ACCEPT_ONLY prompting it to make another call to
* getCredentialElement, this time with the other usage mode. The
* mechanism indicates the missing mode by returning a 0 lifetime for
* it.</b>
*
* @param name the mechanism level name element for the entity whose
* credential is desired. A null value indicates that a mechanism
* dependent default choice is to be made.
* @param initLifetime indicates the lifetime (in seconds) that is
* requested for this credential to be used at the context initiator's
* end. This value should be ignored if the usage is
* ACCEPT_ONLY. Predefined contants are available in the
* org.ietf.jgss.GSSCredential interface.
* @param acceptLifetime indicates the lifetime (in seconds) that is
* requested for this credential to be used at the context acceptor's
* end. This value should be ignored if the usage is
* INITIATE_ONLY. Predefined contants are available in the
* org.ietf.jgss.GSSCredential interface.
* @param usage One of the values GSSCredential.INIATE_ONLY,
* GSSCredential.ACCEPT_ONLY, and GSSCredential.INITIATE_AND_ACCEPT.
* @see org.ietf.jgss.GSSCredential
* @throws GSSException if one of the error situations described in RFC
* 2743 with the GSS_Acquire_Cred or GSS_Add_Cred calls occurs.
*/
public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
int initLifetime, int acceptLifetime, int usage) throws GSSException;
/**
* Creates a name element for this mechanism to be included as part of
* a GSSName implementation. A GSSName is conceptually a container
* class of several name elements from different mechanisms. A GSSName
* can be created either with a String or with a sequence of
* bytes. This factory method accepts the name in a String. Such a name
* can generally be assumed to be printable and may be returned from
* the name element's toString() method.
*
* @param nameStr a string containing the characters describing this
* entity to the mechanism
* @param nameType an Oid serving as a clue as to how the mechanism should
* interpret the nameStr
* @throws GSSException if any of the errors described in RFC 2743 for
* the GSS_Import_Name or GSS_Canonicalize_Name calls occur.
*/
public GSSNameSpi getNameElement(String nameStr, Oid nameType)
throws GSSException;
/**
* This is a variation of the factory method that accepts a String for
* the characters that make up the name. Usually the String characters
* are assumed to be printable. The bytes passed in to this method have
* to be converted to characters using some encoding of the mechanism's
* choice. It is recommended that UTF-8 be used. (Note that UTF-8
* preserves the encoding for 7-bit ASCII characters.)
* <p>
* An exported name will generally be passed in using this method.
*
* @param name the bytes describing this entity to the mechanism
* @param nameType an Oid serving as a clue as to how the mechanism should
* interpret the nameStr
* @throws GSSException if any of the errors described in RFC 2743 for
* the GSS_Import_Name or GSS_Canonicalize_Name calls occur.
*/
public GSSNameSpi getNameElement(byte[] name, Oid nameType)
throws GSSException;
/**
* Creates a security context for this mechanism so that it can be used
* on the context initiator's side.
*
* @param peer the name element from this mechanism that represents the
* peer
* @param myInitiatorCred a credential element for the context
* initiator obtained previously from this mechanism. The identity of
* the context initiator can be obtained from this credential. Passing
* a value of null here indicates that a default entity of the
* mechanism's choice should be assumed to be the context initiator and
* that default credentials should be applied.
* @param lifetime the requested lifetime (in seconds) for the security
* context. Predefined contants are available in the
* org.ietf.jgss.GSSContext interface.
* @throws GSSException if any of the errors described in RFC 2743 in
* the GSS_Init_Sec_Context call occur.
*/
public GSSContextSpi getMechanismContext(GSSNameSpi peer,
GSSCredentialSpi myInitiatorCred,
int lifetime) throws GSSException;
/**
* Creates a security context for this mechanism so thatit can be used
* on the context acceptor's side.
*
* @param myAcceptorCred a credential element for the context acceptor
* obtained previously from this mechanism. The identity of the context
* acceptor cna be obtained from this credential. Passing a value of
* null here indicates that tha default entity of the mechanism's
* choice should be assumed to be the context acceptor and default
* credentials should be applied.
*
* @throws GSSException if any of the errors described in RFC 2743 in
* the GSS_Accept_Sec_Context call occur.
*/
public GSSContextSpi getMechanismContext(GSSCredentialSpi myAcceptorCred)
throws GSSException;
/**
* Creates a security context from a previously exported (serialized)
* security context. Note that this is different from Java
* serialization and is defined at a mechanism level to interoperate
* over the wire with non-Java implementations. Either the initiator or
* the acceptor can export and then import a security context.
* Implementations of mechanism contexts are not required to implement
* exporting and importing.
*
* @param exportedContext the bytes representing this security context
* @throws GSSException is any of the errors described in RFC 2743 in
* the GSS_Import_Sec_Context call occur.
*/
public GSSContextSpi getMechanismContext(byte[] exportedContext)
throws GSSException;
}

View file

@ -0,0 +1,229 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spnego;
import java.io.*;
import java.util.*;
import org.ietf.jgss.*;
import sun.security.jgss.*;
import sun.security.util.*;
/**
* Implements the SPNEGO NegTokenInit token
* as specified in RFC 2478
*
* NegTokenInit ::= SEQUENCE {
* mechTypes [0] MechTypeList OPTIONAL,
* reqFlags [1] ContextFlags OPTIONAL,
* mechToken [2] OCTET STRING OPTIONAL,
* mechListMIC [3] OCTET STRING OPTIONAL
* }
*
* MechTypeList ::= SEQUENCE OF MechType
*
* MechType::= OBJECT IDENTIFIER
*
* ContextFlags ::= BIT STRING {
* delegFlag (0),
* mutualFlag (1),
* replayFlag (2),
* sequenceFlag (3),
* anonFlag (4),
* confFlag (5),
* integFlag (6)
* }
*
* @author Seema Malkani
* @since 1.6
*/
public class NegTokenInit extends SpNegoToken {
// DER-encoded mechTypes
private byte[] mechTypes = null;
private Oid[] mechTypeList = null;
private BitArray reqFlags = null;
private byte[] mechToken = null;
private byte[] mechListMIC = null;
NegTokenInit(byte[] mechTypes, BitArray flags,
byte[] token, byte[] mechListMIC)
{
super(NEG_TOKEN_INIT_ID);
this.mechTypes = mechTypes;
this.reqFlags = flags;
this.mechToken = token;
this.mechListMIC = mechListMIC;
}
// Used by sun.security.jgss.wrapper.NativeGSSContext
// to parse SPNEGO tokens
public NegTokenInit(byte[] in) throws GSSException {
super(NEG_TOKEN_INIT_ID);
parseToken(in);
}
final byte[] encode() throws GSSException {
try {
// create negInitToken
DerOutputStream initToken = new DerOutputStream();
// DER-encoded mechTypes with CONTEXT 00
if (mechTypes != null) {
initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x00), mechTypes);
}
// write context flags with CONTEXT 01
if (reqFlags != null) {
DerOutputStream flags = new DerOutputStream();
flags.putUnalignedBitString(reqFlags);
initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x01), flags);
}
// mechToken with CONTEXT 02
if (mechToken != null) {
DerOutputStream dataValue = new DerOutputStream();
dataValue.putOctetString(mechToken);
initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x02), dataValue);
}
// mechListMIC with CONTEXT 03
if (mechListMIC != null) {
if (DEBUG) {
System.out.println("SpNegoToken NegTokenInit: " +
"sending MechListMIC");
}
DerOutputStream mic = new DerOutputStream();
mic.putOctetString(mechListMIC);
initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x03), mic);
}
// insert in a SEQUENCE
DerOutputStream out = new DerOutputStream();
out.write(DerValue.tag_Sequence, initToken);
return out.toByteArray();
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"Invalid SPNEGO NegTokenInit token : " + e.getMessage());
}
}
private void parseToken(byte[] in) throws GSSException {
try {
DerValue der = new DerValue(in);
// verify NegotiationToken type token
if (!der.isContextSpecific((byte) NEG_TOKEN_INIT_ID)) {
throw new IOException("SPNEGO NegoTokenInit : " +
"did not have right token type");
}
DerValue tmp1 = der.data.getDerValue();
if (tmp1.tag != DerValue.tag_Sequence) {
throw new IOException("SPNEGO NegoTokenInit : " +
"did not have the Sequence tag");
}
// parse various fields if present
int lastField = -1;
while (tmp1.data.available() > 0) {
DerValue tmp2 = tmp1.data.getDerValue();
if (tmp2.isContextSpecific((byte)0x00)) {
// get the DER-encoded sequence of mechTypes
lastField = checkNextField(lastField, 0);
DerInputStream mValue = tmp2.data;
mechTypes = mValue.toByteArray();
// read all the mechTypes
DerValue[] mList = mValue.getSequence(0);
mechTypeList = new Oid[mList.length];
ObjectIdentifier mech = null;
for (int i = 0; i < mList.length; i++) {
mech = mList[i].getOID();
if (DEBUG) {
System.out.println("SpNegoToken NegTokenInit: " +
"reading Mechanism Oid = " + mech);
}
mechTypeList[i] = new Oid(mech.toString());
}
} else if (tmp2.isContextSpecific((byte)0x01)) {
lastField = checkNextField(lastField, 1);
// received reqFlags, skip it
} else if (tmp2.isContextSpecific((byte)0x02)) {
lastField = checkNextField(lastField, 2);
if (DEBUG) {
System.out.println("SpNegoToken NegTokenInit: " +
"reading Mech Token");
}
mechToken = tmp2.data.getOctetString();
} else if (tmp2.isContextSpecific((byte)0x03)) {
lastField = checkNextField(lastField, 3);
if (!GSSUtil.useMSInterop()) {
mechListMIC = tmp2.data.getOctetString();
if (DEBUG) {
System.out.println("SpNegoToken NegTokenInit: " +
"MechListMIC Token = " +
getHexBytes(mechListMIC));
}
}
}
}
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"Invalid SPNEGO NegTokenInit token : " + e.getMessage());
}
}
byte[] getMechTypes() {
return mechTypes;
}
// Used by sun.security.jgss.wrapper.NativeGSSContext
// to find the mechs in SPNEGO tokens
public Oid[] getMechTypeList() {
return mechTypeList;
}
BitArray getReqFlags() {
return reqFlags;
}
// Used by sun.security.jgss.wrapper.NativeGSSContext
// to access the mech token portion of SPNEGO tokens
public byte[] getMechToken() {
return mechToken;
}
byte[] getMechListMIC() {
return mechListMIC;
}
}

View file

@ -0,0 +1,213 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spnego;
import java.io.*;
import java.util.*;
import org.ietf.jgss.*;
import sun.security.jgss.*;
import sun.security.util.*;
/**
* Implements the SPNEGO NegTokenTarg token
* as specified in RFC 2478
*
* NegTokenTarg ::= SEQUENCE {
* negResult [0] ENUMERATED {
* accept_completed (0),
* accept_incomplete (1),
* reject (2) } OPTIONAL,
* supportedMech [1] MechType OPTIONAL,
* responseToken [2] OCTET STRING OPTIONAL,
* mechListMIC [3] OCTET STRING OPTIONAL
* }
*
* MechType::= OBJECT IDENTIFIER
*
*
* @author Seema Malkani
* @since 1.6
*/
public class NegTokenTarg extends SpNegoToken {
private int negResult = 0;
private Oid supportedMech = null;
private byte[] responseToken = null;
private byte[] mechListMIC = null;
NegTokenTarg(int result, Oid mech, byte[] token, byte[] mechListMIC)
{
super(NEG_TOKEN_TARG_ID);
this.negResult = result;
this.supportedMech = mech;
this.responseToken = token;
this.mechListMIC = mechListMIC;
}
// Used by sun.security.jgss.wrapper.NativeGSSContext
// to parse SPNEGO tokens
public NegTokenTarg(byte[] in) throws GSSException {
super(NEG_TOKEN_TARG_ID);
parseToken(in);
}
final byte[] encode() throws GSSException {
try {
// create negTargToken
DerOutputStream targToken = new DerOutputStream();
// write the negotiated result with CONTEXT 00
DerOutputStream result = new DerOutputStream();
result.putEnumerated(negResult);
targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x00), result);
// supportedMech with CONTEXT 01
if (supportedMech != null) {
DerOutputStream mech = new DerOutputStream();
byte[] mechType = supportedMech.getDER();
mech.write(mechType);
targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x01), mech);
}
// response Token with CONTEXT 02
if (responseToken != null) {
DerOutputStream rspToken = new DerOutputStream();
rspToken.putOctetString(responseToken);
targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x02), rspToken);
}
// mechListMIC with CONTEXT 03
if (mechListMIC != null) {
if (DEBUG) {
System.out.println("SpNegoToken NegTokenTarg: " +
"sending MechListMIC");
}
DerOutputStream mic = new DerOutputStream();
mic.putOctetString(mechListMIC);
targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x03), mic);
} else if (GSSUtil.useMSInterop()) {
// required for MS-interoperability
if (responseToken != null) {
if (DEBUG) {
System.out.println("SpNegoToken NegTokenTarg: " +
"sending additional token for MS Interop");
}
DerOutputStream rspToken = new DerOutputStream();
rspToken.putOctetString(responseToken);
targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x03), rspToken);
}
}
// insert in a SEQUENCE
DerOutputStream out = new DerOutputStream();
out.write(DerValue.tag_Sequence, targToken);
return out.toByteArray();
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"Invalid SPNEGO NegTokenTarg token : " + e.getMessage());
}
}
private void parseToken(byte[] in) throws GSSException {
try {
DerValue der = new DerValue(in);
// verify NegotiationToken type token
if (!der.isContextSpecific((byte) NEG_TOKEN_TARG_ID)) {
throw new IOException("SPNEGO NegoTokenTarg : " +
"did not have the right token type");
}
DerValue tmp1 = der.data.getDerValue();
if (tmp1.tag != DerValue.tag_Sequence) {
throw new IOException("SPNEGO NegoTokenTarg : " +
"did not have the Sequence tag");
}
// parse various fields if present
int lastField = -1;
while (tmp1.data.available() > 0) {
DerValue tmp2 = tmp1.data.getDerValue();
if (tmp2.isContextSpecific((byte)0x00)) {
lastField = checkNextField(lastField, 0);
negResult = tmp2.data.getEnumerated();
if (DEBUG) {
System.out.println("SpNegoToken NegTokenTarg: negotiated" +
" result = " + getNegoResultString(negResult));
}
} else if (tmp2.isContextSpecific((byte)0x01)) {
lastField = checkNextField(lastField, 1);
ObjectIdentifier mech = tmp2.data.getOID();
supportedMech = new Oid(mech.toString());
if (DEBUG) {
System.out.println("SpNegoToken NegTokenTarg: " +
"supported mechanism = " + supportedMech);
}
} else if (tmp2.isContextSpecific((byte)0x02)) {
lastField = checkNextField(lastField, 2);
responseToken = tmp2.data.getOctetString();
} else if (tmp2.isContextSpecific((byte)0x03)) {
lastField = checkNextField(lastField, 3);
if (!GSSUtil.useMSInterop()) {
mechListMIC = tmp2.data.getOctetString();
if (DEBUG) {
System.out.println("SpNegoToken NegTokenTarg: " +
"MechListMIC Token = " +
getHexBytes(mechListMIC));
}
}
}
}
} catch (IOException e) {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"Invalid SPNEGO NegTokenTarg token : " + e.getMessage());
}
}
int getNegotiatedResult() {
return negResult;
}
// Used by sun.security.jgss.wrapper.NativeGSSContext
// to find the supported mech in SPNEGO tokens
public Oid getSupportedMech() {
return supportedMech;
}
byte[] getResponseToken() {
return responseToken;
}
byte[] getMechListMIC() {
return mechListMIC;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spnego;
import org.ietf.jgss.*;
import java.security.Provider;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.spi.GSSNameSpi;
import sun.security.jgss.spi.GSSCredentialSpi;
/**
* This class is the cred element implementation for SPNEGO mech.
* NOTE: The current implementation can only support one mechanism.
* This should be changed once multi-mechanism support is needed.
*
* @author Valerie Peng
* @since 1.6
*/
public class SpNegoCredElement implements GSSCredentialSpi {
private GSSCredentialSpi cred = null;
public SpNegoCredElement(GSSCredentialSpi cred) throws GSSException {
this.cred = cred;
}
Oid getInternalMech() {
return cred.getMechanism();
}
// Used by GSSUtil.populateCredentials()
public GSSCredentialSpi getInternalCred() {
return cred;
}
public Provider getProvider() {
return SpNegoMechFactory.PROVIDER;
}
public void dispose() throws GSSException {
cred.dispose();
}
public GSSNameSpi getName() throws GSSException {
return cred.getName();
}
public int getInitLifetime() throws GSSException {
return cred.getInitLifetime();
}
public int getAcceptLifetime() throws GSSException {
return cred.getAcceptLifetime();
}
public boolean isInitiatorCredential() throws GSSException {
return cred.isInitiatorCredential();
}
public boolean isAcceptorCredential() throws GSSException {
return cred.isAcceptorCredential();
}
public Oid getMechanism() {
return GSSUtil.GSS_SPNEGO_MECH_OID;
}
@Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
return cred.impersonate(name);
}
}

View file

@ -0,0 +1,197 @@
/*
* Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spnego;
import org.ietf.jgss.*;
import sun.security.jgss.*;
import sun.security.jgss.spi.*;
import sun.security.jgss.krb5.Krb5MechFactory;
import sun.security.jgss.krb5.Krb5InitCredential;
import sun.security.jgss.krb5.Krb5AcceptCredential;
import sun.security.jgss.krb5.Krb5NameElement;
import java.security.Provider;
import java.util.Vector;
/**
* SpNego Mechanism plug in for JGSS
* This is the properties object required by the JGSS framework.
* All mechanism specific information is defined here.
*
* @author Seema Malkani
* @since 1.6
*/
public final class SpNegoMechFactory implements MechanismFactory {
static final Provider PROVIDER =
new sun.security.jgss.SunProvider();
static final Oid GSS_SPNEGO_MECH_OID =
GSSUtil.createOid("1.3.6.1.5.5.2");
private static Oid[] nameTypes =
new Oid[] { GSSName.NT_USER_NAME,
GSSName.NT_HOSTBASED_SERVICE,
GSSName.NT_EXPORT_NAME};
// The default underlying mech of SPNEGO, must not be SPNEGO itself.
private static final Oid DEFAULT_SPNEGO_MECH_OID =
ProviderList.DEFAULT_MECH_OID.equals(GSS_SPNEGO_MECH_OID)?
GSSUtil.GSS_KRB5_MECH_OID:
ProviderList.DEFAULT_MECH_OID;
// Use an instance of a GSSManager whose provider list
// does not include native provider
final GSSManagerImpl manager;
final Oid[] availableMechs;
private static SpNegoCredElement getCredFromSubject(GSSNameSpi name,
boolean initiate)
throws GSSException {
Vector<SpNegoCredElement> creds =
GSSUtil.searchSubject(name, GSS_SPNEGO_MECH_OID,
initiate, SpNegoCredElement.class);
SpNegoCredElement result = ((creds == null || creds.isEmpty()) ?
null : creds.firstElement());
// Force permission check before returning the cred to caller
if (result != null) {
GSSCredentialSpi cred = result.getInternalCred();
if (GSSUtil.isKerberosMech(cred.getMechanism())) {
if (initiate) {
Krb5InitCredential krbCred = (Krb5InitCredential) cred;
Krb5MechFactory.checkInitCredPermission
((Krb5NameElement) krbCred.getName());
} else {
Krb5AcceptCredential krbCred = (Krb5AcceptCredential) cred;
Krb5MechFactory.checkAcceptCredPermission
((Krb5NameElement) krbCred.getName(), name);
}
}
}
return result;
}
public SpNegoMechFactory() {
this(GSSCaller.CALLER_UNKNOWN);
}
public SpNegoMechFactory(GSSCaller caller) {
manager = new GSSManagerImpl(caller, false);
Oid[] mechs = manager.getMechs();
availableMechs = new Oid[mechs.length-1];
for (int i = 0, j = 0; i < mechs.length; i++) {
// Skip SpNego mechanism
if (!mechs[i].equals(GSS_SPNEGO_MECH_OID)) {
availableMechs[j++] = mechs[i];
}
}
// Move the preferred mech to first place
for (int i=0; i<availableMechs.length; i++) {
if (availableMechs[i].equals(DEFAULT_SPNEGO_MECH_OID)) {
if (i != 0) {
availableMechs[i] = availableMechs[0];
availableMechs[0] = DEFAULT_SPNEGO_MECH_OID;
}
break;
}
}
}
public GSSNameSpi getNameElement(String nameStr, Oid nameType)
throws GSSException {
return manager.getNameElement(
nameStr, nameType, DEFAULT_SPNEGO_MECH_OID);
}
public GSSNameSpi getNameElement(byte[] name, Oid nameType)
throws GSSException {
return manager.getNameElement(name, nameType, DEFAULT_SPNEGO_MECH_OID);
}
public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
int initLifetime, int acceptLifetime,
int usage) throws GSSException {
SpNegoCredElement credElement = getCredFromSubject
(name, (usage != GSSCredential.ACCEPT_ONLY));
if (credElement == null) {
// get CredElement for the default Mechanism
credElement = new SpNegoCredElement
(manager.getCredentialElement(name, initLifetime,
acceptLifetime, null, usage));
}
return credElement;
}
public GSSContextSpi getMechanismContext(GSSNameSpi peer,
GSSCredentialSpi myInitiatorCred, int lifetime)
throws GSSException {
// get SpNego mechanism context
if (myInitiatorCred == null) {
myInitiatorCred = getCredFromSubject(null, true);
} else if (!(myInitiatorCred instanceof SpNegoCredElement)) {
// convert to SpNegoCredElement
SpNegoCredElement cred = new SpNegoCredElement(myInitiatorCred);
return new SpNegoContext(this, peer, cred, lifetime);
}
return new SpNegoContext(this, peer, myInitiatorCred, lifetime);
}
public GSSContextSpi getMechanismContext(GSSCredentialSpi myAcceptorCred)
throws GSSException {
// get SpNego mechanism context
if (myAcceptorCred == null) {
myAcceptorCred = getCredFromSubject(null, false);
} else if (!(myAcceptorCred instanceof SpNegoCredElement)) {
// convert to SpNegoCredElement
SpNegoCredElement cred = new SpNegoCredElement(myAcceptorCred);
return new SpNegoContext(this, cred);
}
return new SpNegoContext(this, myAcceptorCred);
}
public GSSContextSpi getMechanismContext(byte[] exportedContext)
throws GSSException {
// get SpNego mechanism context
return new SpNegoContext(this, exportedContext);
}
public final Oid getMechanismOid() {
return GSS_SPNEGO_MECH_OID;
}
public Provider getProvider() {
return PROVIDER;
}
public Oid[] getNameTypes() {
// nameTypes is cloned in GSSManager.getNamesForMech
return nameTypes;
}
}

View file

@ -0,0 +1,207 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.spnego;
import java.io.*;
import java.util.*;
import org.ietf.jgss.*;
import sun.security.util.*;
import sun.security.jgss.*;
/**
* Astract class for SPNEGO tokens.
* Implementation is based on RFC 2478
*
* NegotiationToken ::= CHOICE {
* negTokenInit [0] NegTokenInit,
* negTokenTarg [1] NegTokenTarg }
*
*
* @author Seema Malkani
* @since 1.6
*/
abstract class SpNegoToken extends GSSToken {
static final int NEG_TOKEN_INIT_ID = 0x00;
static final int NEG_TOKEN_TARG_ID = 0x01;
static enum NegoResult {
ACCEPT_COMPLETE,
ACCEPT_INCOMPLETE,
REJECT,
};
private int tokenType;
// property
static final boolean DEBUG = SpNegoContext.DEBUG;
/**
* The object identifier corresponding to the SPNEGO GSS-API
* mechanism.
*/
public static ObjectIdentifier OID;
static {
try {
OID = new ObjectIdentifier(SpNegoMechFactory.
GSS_SPNEGO_MECH_OID.toString());
} catch (IOException ioe) {
// should not happen
}
}
/**
* Creates SPNEGO token of the specified type.
*/
protected SpNegoToken(int tokenType) {
this.tokenType = tokenType;
}
/**
* Returns the individual encoded SPNEGO token
*
* @return the encoded token
* @exception GSSException
*/
abstract byte[] encode() throws GSSException;
/**
* Returns the encoded SPNEGO token
* Note: inserts the required CHOICE tags
*
* @return the encoded token
* @exception GSSException
*/
byte[] getEncoded() throws IOException, GSSException {
// get the token encoded value
DerOutputStream token = new DerOutputStream();
token.write(encode());
// now insert the CHOICE
switch (tokenType) {
case NEG_TOKEN_INIT_ID:
// Insert CHOICE of Negotiation Token
DerOutputStream initToken = new DerOutputStream();
initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) NEG_TOKEN_INIT_ID), token);
return initToken.toByteArray();
case NEG_TOKEN_TARG_ID:
// Insert CHOICE of Negotiation Token
DerOutputStream targToken = new DerOutputStream();
targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) NEG_TOKEN_TARG_ID), token);
return targToken.toByteArray();
default:
return token.toByteArray();
}
}
/**
* Returns the SPNEGO token type
*
* @return the token type
*/
final int getType() {
return tokenType;
}
/**
* Returns a string representing the token type.
*
* @param tokenType the token type for which a string name is desired
* @return the String name of this token type
*/
static String getTokenName(int type) {
switch (type) {
case NEG_TOKEN_INIT_ID:
return "SPNEGO NegTokenInit";
case NEG_TOKEN_TARG_ID:
return "SPNEGO NegTokenTarg";
default:
return "SPNEGO Mechanism Token";
}
}
/**
* Returns the enumerated type of the Negotiation result.
*
* @param result the negotiated result represented by integer
* @return the enumerated type of Negotiated result
*/
static NegoResult getNegoResultType(int result) {
switch (result) {
case 0:
return NegoResult.ACCEPT_COMPLETE;
case 1:
return NegoResult.ACCEPT_INCOMPLETE;
case 2:
return NegoResult.REJECT;
default:
// unknown - return optimistic result
return NegoResult.ACCEPT_COMPLETE;
}
}
/**
* Returns a string representing the negotiation result.
*
* @param result the negotiated result
* @return the String message of this negotiated result
*/
static String getNegoResultString(int result) {
switch (result) {
case 0:
return "Accept Complete";
case 1:
return "Accept InComplete";
case 2:
return "Reject";
default:
return ("Unknown Negotiated Result: " + result);
}
}
/**
* Checks if the context tag in a sequence is in correct order. The "last"
* value must be smaller than "current".
* @param last the last tag seen
* @param current the current tag
* @return the current tag, used as the next value for last
* @throws GSSException if there's a wrong order
*/
static int checkNextField(int last, int current) throws GSSException {
if (last < current) {
return current;
} else {
throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
"Invalid SpNegoToken token : wrong order");
}
}
}

View file

@ -0,0 +1,143 @@
/*
* Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.wrapper;
import org.ietf.jgss.*;
import java.security.Provider;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.spi.GSSCredentialSpi;
import sun.security.jgss.spi.GSSNameSpi;
/**
* This class is essentially a wrapper class for the gss_cred_id_t
* structure of the native GSS library.
* @author Valerie Peng
* @since 1.6
*/
public class GSSCredElement implements GSSCredentialSpi {
private int usage;
long pCred; // Pointer to the gss_cred_id_t structure
private GSSNameElement name = null;
private GSSLibStub cStub;
// Perform the necessary ServicePermission check on this cred
void doServicePermCheck() throws GSSException {
if (GSSUtil.isKerberosMech(cStub.getMech())) {
if (System.getSecurityManager() != null) {
if (isInitiatorCredential()) {
String tgsName = Krb5Util.getTGSName(name);
Krb5Util.checkServicePermission(tgsName, "initiate");
}
if (isAcceptorCredential() &&
name != GSSNameElement.DEF_ACCEPTOR) {
String krbName = name.getKrbName();
Krb5Util.checkServicePermission(krbName, "accept");
}
}
}
}
// Construct delegation cred using the actual context mech and srcName
GSSCredElement(long pCredentials, GSSNameElement srcName, Oid mech)
throws GSSException {
pCred = pCredentials;
cStub = GSSLibStub.getInstance(mech);
usage = GSSCredential.INITIATE_ONLY;
name = srcName;
}
GSSCredElement(GSSNameElement name, int lifetime, int usage,
GSSLibStub stub) throws GSSException {
cStub = stub;
this.usage = usage;
if (name != null) { // Could be GSSNameElement.DEF_ACCEPTOR
this.name = name;
doServicePermCheck();
pCred = cStub.acquireCred(this.name.pName, lifetime, usage);
} else {
pCred = cStub.acquireCred(0, lifetime, usage);
this.name = new GSSNameElement(cStub.getCredName(pCred), cStub);
doServicePermCheck();
}
}
public Provider getProvider() {
return SunNativeProvider.INSTANCE;
}
public void dispose() throws GSSException {
name = null;
if (pCred != 0) {
pCred = cStub.releaseCred(pCred);
}
}
public GSSNameElement getName() throws GSSException {
return (name == GSSNameElement.DEF_ACCEPTOR ?
null : name);
}
public int getInitLifetime() throws GSSException {
if (isInitiatorCredential()) {
return cStub.getCredTime(pCred);
} else return 0;
}
public int getAcceptLifetime() throws GSSException {
if (isAcceptorCredential()) {
return cStub.getCredTime(pCred);
} else return 0;
}
public boolean isInitiatorCredential() {
return (usage != GSSCredential.ACCEPT_ONLY);
}
public boolean isAcceptorCredential() {
return (usage != GSSCredential.INITIATE_ONLY);
}
public Oid getMechanism() {
return cStub.getMech();
}
public String toString() {
// No hex bytes available for native impl
return "N/A";
}
@SuppressWarnings("deprecation")
protected void finalize() throws Throwable {
dispose();
}
@Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
throw new GSSException(GSSException.FAILURE, -1,
"Not supported yet");
}
}

View file

@ -0,0 +1,126 @@
/*
* Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.wrapper;
import java.util.Hashtable;
import org.ietf.jgss.Oid;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.ChannelBinding;
import org.ietf.jgss.MessageProp;
import org.ietf.jgss.GSSException;
import sun.security.jgss.GSSUtil;
/**
* This class is essentially a JNI calling stub for all wrapper classes.
*
* @author Valerie Peng
* @since 1.6
*/
class GSSLibStub {
private Oid mech;
private long pMech;
/**
* Initialization routine to dynamically load function pointers.
*
* @param lib library name to dlopen
* @param debug set to true for reporting native debugging info
* @return true if succeeded, false otherwise.
*/
static native boolean init(String lib, boolean debug);
private static native long getMechPtr(byte[] oidDerEncoding);
// Miscellaneous routines
static native Oid[] indicateMechs();
native Oid[] inquireNamesForMech() throws GSSException;
// Name related routines
native void releaseName(long pName);
native long importName(byte[] name, Oid type);
native boolean compareName(long pName1, long pName2);
native long canonicalizeName(long pName);
native byte[] exportName(long pName) throws GSSException;
native Object[] displayName(long pName) throws GSSException;
// Credential related routines
native long acquireCred(long pName, int lifetime, int usage)
throws GSSException;
native long releaseCred(long pCred);
native long getCredName(long pCred);
native int getCredTime(long pCred);
native int getCredUsage(long pCred);
// Context related routines
native NativeGSSContext importContext(byte[] interProcToken);
native byte[] initContext(long pCred, long targetName, ChannelBinding cb,
byte[] inToken, NativeGSSContext context);
native byte[] acceptContext(long pCred, ChannelBinding cb,
byte[] inToken, NativeGSSContext context);
native long[] inquireContext(long pContext);
native Oid getContextMech(long pContext);
native long getContextName(long pContext, boolean isSrc);
native int getContextTime(long pContext);
native long deleteContext(long pContext);
native int wrapSizeLimit(long pContext, int flags, int qop, int outSize);
native byte[] exportContext(long pContext);
native byte[] getMic(long pContext, int qop, byte[] msg);
native void verifyMic(long pContext, byte[] token, byte[] msg,
MessageProp prop) ;
native byte[] wrap(long pContext, byte[] msg, MessageProp prop);
native byte[] unwrap(long pContext, byte[] msgToken, MessageProp prop);
private static Hashtable<Oid, GSSLibStub>
table = new Hashtable<Oid, GSSLibStub>(5);
static GSSLibStub getInstance(Oid mech) throws GSSException {
GSSLibStub s = table.get(mech);
if (s == null) {
s = new GSSLibStub(mech);
table.put(mech, s);
}
return s;
}
private GSSLibStub(Oid mech) throws GSSException {
SunNativeProvider.debug("Created GSSLibStub for mech " + mech);
this.mech = mech;
this.pMech = getMechPtr(mech.getDER());
}
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof GSSLibStub)) {
return false;
}
return (mech.equals(((GSSLibStub) obj).getMech()));
}
public int hashCode() {
return mech.hashCode();
}
Oid getMech() {
return mech;
}
}

View file

@ -0,0 +1,296 @@
/*
* Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.wrapper;
import org.ietf.jgss.*;
import java.security.Provider;
import java.security.Security;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import sun.security.krb5.Realm;
import sun.security.jgss.GSSUtil;
import sun.security.util.ObjectIdentifier;
import sun.security.util.DerInputStream;
import sun.security.util.DerOutputStream;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.GSSExceptionImpl;
import sun.security.jgss.spi.GSSNameSpi;
import javax.security.auth.kerberos.ServicePermission;
/**
* This class is essentially a wrapper class for the gss_name_t
* structure of the native GSS library.
* @author Valerie Peng
* @since 1.6
*/
public class GSSNameElement implements GSSNameSpi {
long pName = 0; // Pointer to the gss_name_t structure
private String printableName;
private Oid printableType;
private GSSLibStub cStub;
static final GSSNameElement DEF_ACCEPTOR = new GSSNameElement();
private static Oid getNativeNameType(Oid nameType, GSSLibStub stub) {
if (GSSUtil.NT_GSS_KRB5_PRINCIPAL.equals(nameType)) {
Oid[] supportedNTs = null;
try {
supportedNTs = stub.inquireNamesForMech();
} catch (GSSException ge) {
if (ge.getMajor() == GSSException.BAD_MECH &&
GSSUtil.isSpNegoMech(stub.getMech())) {
// Workaround known Heimdal issue and retry with KRB5
try {
stub = GSSLibStub.getInstance
(GSSUtil.GSS_KRB5_MECH_OID);
supportedNTs = stub.inquireNamesForMech();
} catch (GSSException ge2) {
// Should never happen
SunNativeProvider.debug("Name type list unavailable: " +
ge2.getMajorString());
}
} else {
SunNativeProvider.debug("Name type list unavailable: " +
ge.getMajorString());
}
}
if (supportedNTs != null) {
for (int i = 0; i < supportedNTs.length; i++) {
if (supportedNTs[i].equals(nameType)) return nameType;
}
// Special handling the specified name type
SunNativeProvider.debug("Override " + nameType +
" with mechanism default(null)");
return null; // Use mechanism specific default
}
}
return nameType;
}
private GSSNameElement() {
printableName = "<DEFAULT ACCEPTOR>";
}
GSSNameElement(long pNativeName, GSSLibStub stub) throws GSSException {
assert(stub != null);
if (pNativeName == 0) {
throw new GSSException(GSSException.BAD_NAME);
}
// Note: pNativeName is assumed to be a MN.
pName = pNativeName;
cStub = stub;
setPrintables();
}
GSSNameElement(byte[] nameBytes, Oid nameType, GSSLibStub stub)
throws GSSException {
assert(stub != null);
if (nameBytes == null) {
throw new GSSException(GSSException.BAD_NAME);
}
cStub = stub;
byte[] name = nameBytes;
if (nameType != null) {
// Special handling the specified name type if
// necessary
nameType = getNativeNameType(nameType, stub);
if (GSSName.NT_EXPORT_NAME.equals(nameType)) {
// Need to add back the mech Oid portion (stripped
// off by GSSNameImpl class prior to calling this
// method) for "NT_EXPORT_NAME"
byte[] mechBytes = null;
DerOutputStream dout = new DerOutputStream();
Oid mech = cStub.getMech();
try {
dout.putOID(new ObjectIdentifier(mech.toString()));
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.FAILURE, e);
}
mechBytes = dout.toByteArray();
name = new byte[2 + 2 + mechBytes.length + 4 + nameBytes.length];
int pos = 0;
name[pos++] = 0x04;
name[pos++] = 0x01;
name[pos++] = (byte) (mechBytes.length>>>8);
name[pos++] = (byte) mechBytes.length;
System.arraycopy(mechBytes, 0, name, pos, mechBytes.length);
pos += mechBytes.length;
name[pos++] = (byte) (nameBytes.length>>>24);
name[pos++] = (byte) (nameBytes.length>>>16);
name[pos++] = (byte) (nameBytes.length>>>8);
name[pos++] = (byte) nameBytes.length;
System.arraycopy(nameBytes, 0, name, pos, nameBytes.length);
}
}
pName = cStub.importName(name, nameType);
setPrintables();
SecurityManager sm = System.getSecurityManager();
if (sm != null && !Realm.AUTODEDUCEREALM) {
String krbName = getKrbName();
int atPos = krbName.lastIndexOf('@');
if (atPos != -1) {
String atRealm = krbName.substring(atPos);
// getNativeNameType() can modify NT_GSS_KRB5_PRINCIPAL to null
if ((nameType == null
|| nameType.equals(GSSUtil.NT_GSS_KRB5_PRINCIPAL))
&& new String(nameBytes).endsWith(atRealm)) {
// Created from Kerberos name with realm, no need to check
} else {
try {
sm.checkPermission(new ServicePermission(atRealm, "-"));
} catch (SecurityException se) {
// Do not chain the actual exception to hide info
throw new GSSException(GSSException.FAILURE);
}
}
}
}
SunNativeProvider.debug("Imported " + printableName + " w/ type " +
printableType);
}
private void setPrintables() throws GSSException {
Object[] printables = null;
printables = cStub.displayName(pName);
assert((printables != null) && (printables.length == 2));
printableName = (String) printables[0];
assert(printableName != null);
printableType = (Oid) printables[1];
if (printableType == null) {
printableType = GSSName.NT_USER_NAME;
}
}
// Need to be public for GSSUtil.getSubject()
public String getKrbName() throws GSSException {
long mName = 0;
GSSLibStub stub = cStub;
if (!GSSUtil.isKerberosMech(cStub.getMech())) {
stub = GSSLibStub.getInstance(GSSUtil.GSS_KRB5_MECH_OID);
}
mName = stub.canonicalizeName(pName);
Object[] printables2 = stub.displayName(mName);
stub.releaseName(mName);
SunNativeProvider.debug("Got kerberized name: " + printables2[0]);
return (String) printables2[0];
}
public Provider getProvider() {
return SunNativeProvider.INSTANCE;
}
public boolean equals(GSSNameSpi other) throws GSSException {
if (!(other instanceof GSSNameElement)) {
return false;
}
return cStub.compareName(pName, ((GSSNameElement)other).pName);
}
public boolean equals(Object other) {
if (!(other instanceof GSSNameElement)) {
return false;
}
try {
return equals((GSSNameElement) other);
} catch (GSSException ex) {
return false;
}
}
public int hashCode() {
return Long.hashCode(pName);
}
public byte[] export() throws GSSException {
byte[] nameVal = cStub.exportName(pName);
// Need to strip off the mech Oid portion of the exported
// bytes since GSSNameImpl class will subsequently add it.
int pos = 0;
if ((nameVal[pos++] != 0x04) ||
(nameVal[pos++] != 0x01))
throw new GSSException(GSSException.BAD_NAME);
int mechOidLen = (((0xFF & nameVal[pos++]) << 8) |
(0xFF & nameVal[pos++]));
ObjectIdentifier temp = null;
try {
DerInputStream din = new DerInputStream(nameVal, pos,
mechOidLen);
temp = new ObjectIdentifier(din);
} catch (IOException e) {
throw new GSSExceptionImpl(GSSException.BAD_NAME, e);
}
Oid mech2 = new Oid(temp.toString());
assert(mech2.equals(getMechanism()));
pos += mechOidLen;
int mechPortionLen = (((0xFF & nameVal[pos++]) << 24) |
((0xFF & nameVal[pos++]) << 16) |
((0xFF & nameVal[pos++]) << 8) |
(0xFF & nameVal[pos++]));
if (mechPortionLen < 0) {
throw new GSSException(GSSException.BAD_NAME);
}
byte[] mechPortion = new byte[mechPortionLen];
System.arraycopy(nameVal, pos, mechPortion, 0, mechPortionLen);
return mechPortion;
}
public Oid getMechanism() {
return cStub.getMech();
}
public String toString() {
return printableName;
}
public Oid getStringNameType() {
return printableType;
}
public boolean isAnonymousName() {
return (GSSName.NT_ANONYMOUS.equals(printableType));
}
public void dispose() {
if (pName != 0) {
cStub.releaseName(pName);
pName = 0;
}
}
@SuppressWarnings("deprecation")
protected void finalize() throws Throwable {
dispose();
}
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.wrapper;
import org.ietf.jgss.*;
import javax.security.auth.kerberos.ServicePermission;
/**
* This class is an utility class for Kerberos related stuff.
* @author Valerie Peng
* @since 1.6
*/
class Krb5Util {
// Return the Kerberos TGS principal name using the domain
// of the specified <code>name</code>
static String getTGSName(GSSNameElement name)
throws GSSException {
String krbPrinc = name.getKrbName();
int atIndex = krbPrinc.indexOf('@');
String realm = krbPrinc.substring(atIndex + 1);
StringBuilder sb = new StringBuilder("krbtgt/");
sb.append(realm).append('@').append(realm);
return sb.toString();
}
// Perform the Service Permission check using the specified
// <code>target</code> and <code>action</code>
static void checkServicePermission(String target, String action) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
SunNativeProvider.debug("Checking ServicePermission(" +
target + ", " + action + ")");
ServicePermission perm =
new ServicePermission(target, action);
sm.checkPermission(perm);
}
}
}

View file

@ -0,0 +1,631 @@
/*
* Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.wrapper;
import org.ietf.jgss.*;
import java.security.Provider;
import sun.security.jgss.GSSHeader;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.GSSExceptionImpl;
import sun.security.jgss.spi.*;
import sun.security.util.DerValue;
import sun.security.util.ObjectIdentifier;
import sun.security.jgss.spnego.NegTokenInit;
import sun.security.jgss.spnego.NegTokenTarg;
import javax.security.auth.kerberos.DelegationPermission;
import java.io.*;
/**
* This class is essentially a wrapper class for the gss_ctx_id_t
* structure of the native GSS library.
* @author Valerie Peng
* @since 1.6
*/
class NativeGSSContext implements GSSContextSpi {
private static final int GSS_C_DELEG_FLAG = 1;
private static final int GSS_C_MUTUAL_FLAG = 2;
private static final int GSS_C_REPLAY_FLAG = 4;
private static final int GSS_C_SEQUENCE_FLAG = 8;
private static final int GSS_C_CONF_FLAG = 16;
private static final int GSS_C_INTEG_FLAG = 32;
private static final int GSS_C_ANON_FLAG = 64;
private static final int GSS_C_PROT_READY_FLAG = 128;
private static final int GSS_C_TRANS_FLAG = 256;
private static final int NUM_OF_INQUIRE_VALUES = 6;
private long pContext = 0; // Pointer to the gss_ctx_id_t structure
private GSSNameElement srcName;
private GSSNameElement targetName;
private GSSCredElement cred;
private boolean isInitiator;
private boolean isEstablished;
private Oid actualMech; // Assigned during context establishment
private ChannelBinding cb;
private GSSCredElement delegatedCred;
private int flags;
private int lifetime = GSSCredential.DEFAULT_LIFETIME;
private final GSSLibStub cStub;
private boolean skipDelegPermCheck;
private boolean skipServicePermCheck;
// Retrieve the (preferred) mech out of SPNEGO tokens, i.e.
// NegTokenInit & NegTokenTarg
private static Oid getMechFromSpNegoToken(byte[] token,
boolean isInitiator)
throws GSSException {
Oid mech = null;
if (isInitiator) {
GSSHeader header = null;
try {
header = new GSSHeader(new ByteArrayInputStream(token));
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
int negTokenLen = header.getMechTokenLength();
byte[] negToken = new byte[negTokenLen];
System.arraycopy(token, token.length-negTokenLen,
negToken, 0, negToken.length);
NegTokenInit ntok = new NegTokenInit(negToken);
if (ntok.getMechToken() != null) {
Oid[] mechList = ntok.getMechTypeList();
mech = mechList[0];
}
} else {
NegTokenTarg ntok = new NegTokenTarg(token);
mech = ntok.getSupportedMech();
}
return mech;
}
// Perform the Service permission check
private void doServicePermCheck() throws GSSException {
if (System.getSecurityManager() != null) {
String action = (isInitiator? "initiate" : "accept");
// Need to check Service permission for accessing
// initiator cred for SPNEGO during context establishment
if (GSSUtil.isSpNegoMech(cStub.getMech()) && isInitiator
&& !isEstablished) {
if (srcName == null) {
// Check by creating default initiator KRB5 cred
GSSCredElement tempCred =
new GSSCredElement(null, lifetime,
GSSCredential.INITIATE_ONLY,
GSSLibStub.getInstance(GSSUtil.GSS_KRB5_MECH_OID));
tempCred.dispose();
} else {
String tgsName = Krb5Util.getTGSName(srcName);
Krb5Util.checkServicePermission(tgsName, action);
}
}
String targetStr = targetName.getKrbName();
Krb5Util.checkServicePermission(targetStr, action);
skipServicePermCheck = true;
}
}
// Perform the Delegation permission check
private void doDelegPermCheck() throws GSSException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
String targetStr = targetName.getKrbName();
String tgsStr = Krb5Util.getTGSName(targetName);
StringBuilder sb = new StringBuilder("\"");
sb.append(targetStr).append("\" \"");
sb.append(tgsStr).append('\"');
String krbPrincPair = sb.toString();
SunNativeProvider.debug("Checking DelegationPermission (" +
krbPrincPair + ")");
DelegationPermission perm =
new DelegationPermission(krbPrincPair);
sm.checkPermission(perm);
skipDelegPermCheck = true;
}
}
private byte[] retrieveToken(InputStream is, int mechTokenLen)
throws GSSException {
try {
byte[] result = null;
if (mechTokenLen != -1) {
// Need to add back the GSS header for a complete GSS token
SunNativeProvider.debug("Precomputed mechToken length: " +
mechTokenLen);
GSSHeader gssHeader = new GSSHeader
(new ObjectIdentifier(cStub.getMech().toString()),
mechTokenLen);
ByteArrayOutputStream baos = new ByteArrayOutputStream(600);
byte[] mechToken = new byte[mechTokenLen];
int len = is.read(mechToken);
assert(mechTokenLen == len);
gssHeader.encode(baos);
baos.write(mechToken);
result = baos.toByteArray();
} else {
// Must be unparsed GSS token or SPNEGO's NegTokenTarg token
assert(mechTokenLen == -1);
DerValue dv = new DerValue(is);
result = dv.toByteArray();
}
SunNativeProvider.debug("Complete Token length: " +
result.length);
return result;
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
// Constructor for context initiator
NativeGSSContext(GSSNameElement peer, GSSCredElement myCred,
int time, GSSLibStub stub) throws GSSException {
if (peer == null) {
throw new GSSException(GSSException.FAILURE, 1, "null peer");
}
cStub = stub;
cred = myCred;
targetName = peer;
isInitiator = true;
lifetime = time;
if (GSSUtil.isKerberosMech(cStub.getMech())) {
doServicePermCheck();
if (cred == null) {
cred = new GSSCredElement(null, lifetime,
GSSCredential.INITIATE_ONLY, cStub);
}
srcName = cred.getName();
}
}
// Constructor for context acceptor
NativeGSSContext(GSSCredElement myCred, GSSLibStub stub)
throws GSSException {
cStub = stub;
cred = myCred;
if (cred != null) targetName = cred.getName();
isInitiator = false;
// Defer Service permission check for default acceptor cred
// to acceptSecContext()
if (GSSUtil.isKerberosMech(cStub.getMech()) && targetName != null) {
doServicePermCheck();
}
// srcName and potentially targetName (when myCred is null)
// will be set in GSSLibStub.acceptContext(...)
}
// Constructor for imported context
NativeGSSContext(long pCtxt, GSSLibStub stub) throws GSSException {
assert(pContext != 0);
pContext = pCtxt;
cStub = stub;
// Set everything except cred, cb, delegatedCred
long[] info = cStub.inquireContext(pContext);
if (info.length != NUM_OF_INQUIRE_VALUES) {
throw new RuntimeException("Bug w/ GSSLibStub.inquireContext()");
}
srcName = new GSSNameElement(info[0], cStub);
targetName = new GSSNameElement(info[1], cStub);
isInitiator = (info[2] != 0);
isEstablished = (info[3] != 0);
flags = (int) info[4];
lifetime = (int) info[5];
// Do Service Permission check when importing SPNEGO context
// just to be safe
Oid mech = cStub.getMech();
if (GSSUtil.isSpNegoMech(mech) || GSSUtil.isKerberosMech(mech)) {
doServicePermCheck();
}
}
public Provider getProvider() {
return SunNativeProvider.INSTANCE;
}
public byte[] initSecContext(InputStream is, int mechTokenLen)
throws GSSException {
byte[] outToken = null;
if ((!isEstablished) && (isInitiator)) {
byte[] inToken = null;
// Ignore the specified input stream on the first call
if (pContext != 0) {
inToken = retrieveToken(is, mechTokenLen);
SunNativeProvider.debug("initSecContext=> inToken len=" +
inToken.length);
}
if (!getCredDelegState()) skipDelegPermCheck = true;
if (GSSUtil.isKerberosMech(cStub.getMech()) && !skipDelegPermCheck) {
doDelegPermCheck();
}
long pCred = (cred == null? 0 : cred.pCred);
outToken = cStub.initContext(pCred, targetName.pName,
cb, inToken, this);
SunNativeProvider.debug("initSecContext=> outToken len=" +
(outToken == null ? 0 : outToken.length));
// Only inspect the token when the permission check
// has not been performed
if (GSSUtil.isSpNegoMech(cStub.getMech()) && outToken != null) {
// WORKAROUND for SEAM bug#6287358
actualMech = getMechFromSpNegoToken(outToken, true);
if (GSSUtil.isKerberosMech(actualMech)) {
if (!skipServicePermCheck) doServicePermCheck();
if (!skipDelegPermCheck) doDelegPermCheck();
}
}
if (isEstablished) {
if (srcName == null) {
srcName = new GSSNameElement
(cStub.getContextName(pContext, true), cStub);
}
if (cred == null) {
cred = new GSSCredElement(srcName, lifetime,
GSSCredential.INITIATE_ONLY,
cStub);
}
}
}
return outToken;
}
public byte[] acceptSecContext(InputStream is, int mechTokenLen)
throws GSSException {
byte[] outToken = null;
if ((!isEstablished) && (!isInitiator)) {
byte[] inToken = retrieveToken(is, mechTokenLen);
SunNativeProvider.debug("acceptSecContext=> inToken len=" +
inToken.length);
long pCred = (cred == null? 0 : cred.pCred);
outToken = cStub.acceptContext(pCred, cb, inToken, this);
SunNativeProvider.debug("acceptSecContext=> outToken len=" +
(outToken == null? 0 : outToken.length));
if (targetName == null) {
targetName = new GSSNameElement
(cStub.getContextName(pContext, false), cStub);
// Replace the current default acceptor cred now that
// the context acceptor name is available
if (cred != null) cred.dispose();
cred = new GSSCredElement(targetName, lifetime,
GSSCredential.ACCEPT_ONLY, cStub);
}
// Only inspect token when the permission check has not
// been performed
if (GSSUtil.isSpNegoMech(cStub.getMech()) &&
(outToken != null) && !skipServicePermCheck) {
if (GSSUtil.isKerberosMech(getMechFromSpNegoToken
(outToken, false))) {
doServicePermCheck();
}
}
}
return outToken;
}
public boolean isEstablished() {
return isEstablished;
}
public void dispose() throws GSSException {
srcName = null;
targetName = null;
cred = null;
delegatedCred = null;
if (pContext != 0) {
pContext = cStub.deleteContext(pContext);
pContext = 0;
}
}
public int getWrapSizeLimit(int qop, boolean confReq,
int maxTokenSize)
throws GSSException {
return cStub.wrapSizeLimit(pContext, (confReq? 1:0), qop,
maxTokenSize);
}
public byte[] wrap(byte[] inBuf, int offset, int len,
MessageProp msgProp) throws GSSException {
byte[] data = inBuf;
if ((offset != 0) || (len != inBuf.length)) {
data = new byte[len];
System.arraycopy(inBuf, offset, data, 0, len);
}
return cStub.wrap(pContext, data, msgProp);
}
public void wrap(byte[] inBuf, int offset, int len,
OutputStream os, MessageProp msgProp)
throws GSSException {
try {
byte[] result = wrap(inBuf, offset, len, msgProp);
os.write(result);
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
public int wrap(byte[] inBuf, int inOffset, int len, byte[] outBuf,
int outOffset, MessageProp msgProp)
throws GSSException {
byte[] result = wrap(inBuf, inOffset, len, msgProp);
System.arraycopy(result, 0, outBuf, outOffset, result.length);
return result.length;
}
public void wrap(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
try {
byte[] data = new byte[inStream.available()];
int length = inStream.read(data);
byte[] token = wrap(data, 0, length, msgProp);
outStream.write(token);
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
public byte[] unwrap(byte[] inBuf, int offset, int len,
MessageProp msgProp)
throws GSSException {
if ((offset != 0) || (len != inBuf.length)) {
byte[] temp = new byte[len];
System.arraycopy(inBuf, offset, temp, 0, len);
return cStub.unwrap(pContext, temp, msgProp);
} else {
return cStub.unwrap(pContext, inBuf, msgProp);
}
}
public int unwrap(byte[] inBuf, int inOffset, int len,
byte[] outBuf, int outOffset,
MessageProp msgProp) throws GSSException {
byte[] result = null;
if ((inOffset != 0) || (len != inBuf.length)) {
byte[] temp = new byte[len];
System.arraycopy(inBuf, inOffset, temp, 0, len);
result = cStub.unwrap(pContext, temp, msgProp);
} else {
result = cStub.unwrap(pContext, inBuf, msgProp);
}
System.arraycopy(result, 0, outBuf, outOffset, result.length);
return result.length;
}
public void unwrap(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
try {
byte[] wrapped = new byte[inStream.available()];
int wLength = inStream.read(wrapped);
byte[] data = unwrap(wrapped, 0, wLength, msgProp);
outStream.write(data);
outStream.flush();
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
public int unwrap(InputStream inStream,
byte[] outBuf, int outOffset,
MessageProp msgProp) throws GSSException {
byte[] wrapped = null;
int wLength = 0;
try {
wrapped = new byte[inStream.available()];
wLength = inStream.read(wrapped);
byte[] result = unwrap(wrapped, 0, wLength, msgProp);
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
byte[] result = unwrap(wrapped, 0, wLength, msgProp);
System.arraycopy(result, 0, outBuf, outOffset, result.length);
return result.length;
}
public byte[] getMIC(byte[] in, int offset, int len,
MessageProp msgProp) throws GSSException {
int qop = (msgProp == null? 0:msgProp.getQOP());
byte[] inMsg = in;
if ((offset != 0) || (len != in.length)) {
inMsg = new byte[len];
System.arraycopy(in, offset, inMsg, 0, len);
}
return cStub.getMic(pContext, qop, inMsg);
}
public void getMIC(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException {
try {
int length = 0;
byte[] msg = new byte[inStream.available()];
length = inStream.read(msg);
byte[] msgToken = getMIC(msg, 0, length, msgProp);
if ((msgToken != null) && msgToken.length != 0) {
outStream.write(msgToken);
}
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
public void verifyMIC(byte[] inToken, int tOffset, int tLen,
byte[] inMsg, int mOffset, int mLen,
MessageProp msgProp) throws GSSException {
byte[] token = inToken;
byte[] msg = inMsg;
if ((tOffset != 0) || (tLen != inToken.length)) {
token = new byte[tLen];
System.arraycopy(inToken, tOffset, token, 0, tLen);
}
if ((mOffset != 0) || (mLen != inMsg.length)) {
msg = new byte[mLen];
System.arraycopy(inMsg, mOffset, msg, 0, mLen);
}
cStub.verifyMic(pContext, token, msg, msgProp);
}
public void verifyMIC(InputStream tokStream, InputStream msgStream,
MessageProp msgProp) throws GSSException {
try {
byte[] msg = new byte[msgStream.available()];
int mLength = msgStream.read(msg);
byte[] tok = new byte[tokStream.available()];
int tLength = tokStream.read(tok);
verifyMIC(tok, 0, tLength, msg, 0, mLength, msgProp);
} catch (IOException ioe) {
throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
}
}
public byte[] export() throws GSSException {
byte[] result = cStub.exportContext(pContext);
pContext = 0;
return result;
}
private void changeFlags(int flagMask, boolean isEnable) {
if (isInitiator && pContext == 0) {
if (isEnable) {
flags |= flagMask;
} else {
flags &= ~flagMask;
}
}
}
public void requestMutualAuth(boolean state) throws GSSException {
changeFlags(GSS_C_MUTUAL_FLAG, state);
}
public void requestReplayDet(boolean state) throws GSSException {
changeFlags(GSS_C_REPLAY_FLAG, state);
}
public void requestSequenceDet(boolean state) throws GSSException {
changeFlags(GSS_C_SEQUENCE_FLAG, state);
}
public void requestCredDeleg(boolean state) throws GSSException {
changeFlags(GSS_C_DELEG_FLAG, state);
}
public void requestAnonymity(boolean state) throws GSSException {
changeFlags(GSS_C_ANON_FLAG, state);
}
public void requestConf(boolean state) throws GSSException {
changeFlags(GSS_C_CONF_FLAG, state);
}
public void requestInteg(boolean state) throws GSSException {
changeFlags(GSS_C_INTEG_FLAG, state);
}
public void requestDelegPolicy(boolean state) throws GSSException {
// Not supported, ignore
}
public void requestLifetime(int lifetime) throws GSSException {
if (isInitiator && pContext == 0) {
this.lifetime = lifetime;
}
}
public void setChannelBinding(ChannelBinding cb) throws GSSException {
if (pContext == 0) {
this.cb = cb;
}
}
private boolean checkFlags(int flagMask) {
return ((flags & flagMask) != 0);
}
public boolean getCredDelegState() {
return checkFlags(GSS_C_DELEG_FLAG);
}
public boolean getMutualAuthState() {
return checkFlags(GSS_C_MUTUAL_FLAG);
}
public boolean getReplayDetState() {
return checkFlags(GSS_C_REPLAY_FLAG);
}
public boolean getSequenceDetState() {
return checkFlags(GSS_C_SEQUENCE_FLAG);
}
public boolean getAnonymityState() {
return checkFlags(GSS_C_ANON_FLAG);
}
public boolean isTransferable() throws GSSException {
return checkFlags(GSS_C_TRANS_FLAG);
}
public boolean isProtReady() {
return checkFlags(GSS_C_PROT_READY_FLAG);
}
public boolean getConfState() {
return checkFlags(GSS_C_CONF_FLAG);
}
public boolean getIntegState() {
return checkFlags(GSS_C_INTEG_FLAG);
}
public boolean getDelegPolicyState() {
return false;
}
public int getLifetime() {
return cStub.getContextTime(pContext);
}
public GSSNameSpi getSrcName() throws GSSException {
return srcName;
}
public GSSNameSpi getTargName() throws GSSException {
return targetName;
}
public Oid getMech() throws GSSException {
if (isEstablished && actualMech != null) {
return actualMech;
} else {
return cStub.getMech();
}
}
public GSSCredentialSpi getDelegCred() throws GSSException {
return delegatedCred;
}
public boolean isInitiator() {
return isInitiator;
}
@SuppressWarnings("deprecation")
protected void finalize() throws Throwable {
dispose();
}
public Object inquireSecContext(String type)
throws GSSException {
throw new GSSException(GSSException.UNAVAILABLE, -1,
"Inquire type not supported.");
}
}

View file

@ -0,0 +1,183 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.wrapper;
import java.io.UnsupportedEncodingException;
import java.security.Provider;
import java.util.Vector;
import org.ietf.jgss.*;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.GSSCaller;
import sun.security.jgss.GSSExceptionImpl;
import sun.security.jgss.spi.*;
/**
* JGSS plugin for generic mechanisms provided through native GSS framework.
*
* @author Valerie Peng
*/
public final class NativeGSSFactory implements MechanismFactory {
GSSLibStub cStub = null;
private final GSSCaller caller;
private GSSCredElement getCredFromSubject(GSSNameElement name,
boolean initiate)
throws GSSException {
Oid mech = cStub.getMech();
Vector<GSSCredElement> creds = GSSUtil.searchSubject
(name, mech, initiate, GSSCredElement.class);
// If Subject is present but no native creds available
if (creds != null && creds.isEmpty()) {
if (GSSUtil.useSubjectCredsOnly(caller)) {
throw new GSSException(GSSException.NO_CRED);
}
}
GSSCredElement result = ((creds == null || creds.isEmpty()) ?
null : creds.firstElement());
// Force permission check before returning the cred to caller
if (result != null) {
result.doServicePermCheck();
}
return result;
}
public NativeGSSFactory(GSSCaller caller) {
this.caller = caller;
// Have to call setMech(Oid) explicitly before calling other
// methods. Otherwise, NPE may be thrown unexpectantly
}
public void setMech(Oid mech) throws GSSException {
cStub = GSSLibStub.getInstance(mech);
}
public GSSNameSpi getNameElement(String nameStr, Oid nameType)
throws GSSException {
try {
byte[] nameBytes =
(nameStr == null ? null : nameStr.getBytes("UTF-8"));
return new GSSNameElement(nameBytes, nameType, cStub);
} catch (UnsupportedEncodingException uee) {
// Shouldn't happen
throw new GSSExceptionImpl(GSSException.FAILURE, uee);
}
}
public GSSNameSpi getNameElement(byte[] name, Oid nameType)
throws GSSException {
return new GSSNameElement(name, nameType, cStub);
}
public GSSCredentialSpi getCredentialElement(GSSNameSpi name,
int initLifetime,
int acceptLifetime,
int usage)
throws GSSException {
GSSNameElement nname = null;
if (name != null && !(name instanceof GSSNameElement)) {
nname = (GSSNameElement)
getNameElement(name.toString(), name.getStringNameType());
} else nname = (GSSNameElement) name;
if (usage == GSSCredential.INITIATE_AND_ACCEPT) {
// Force separate acqusition of cred element since
// MIT's impl does not correctly report NO_CRED error.
usage = GSSCredential.INITIATE_ONLY;
}
GSSCredElement credElement =
getCredFromSubject(nname, (usage == GSSCredential.INITIATE_ONLY));
if (credElement == null) {
// No cred in the Subject
if (usage == GSSCredential.INITIATE_ONLY) {
credElement = new GSSCredElement(nname, initLifetime,
usage, cStub);
} else if (usage == GSSCredential.ACCEPT_ONLY) {
if (nname == null) {
nname = GSSNameElement.DEF_ACCEPTOR;
}
credElement = new GSSCredElement(nname, acceptLifetime,
usage, cStub);
} else {
throw new GSSException(GSSException.FAILURE, -1,
"Unknown usage mode requested");
}
}
return credElement;
}
public GSSContextSpi getMechanismContext(GSSNameSpi peer,
GSSCredentialSpi myCred,
int lifetime)
throws GSSException {
if (peer == null) {
throw new GSSException(GSSException.BAD_NAME);
} else if (!(peer instanceof GSSNameElement)) {
peer = (GSSNameElement)
getNameElement(peer.toString(), peer.getStringNameType());
}
if (myCred == null) {
myCred = getCredFromSubject(null, true);
} else if (!(myCred instanceof GSSCredElement)) {
throw new GSSException(GSSException.NO_CRED);
}
return new NativeGSSContext((GSSNameElement) peer,
(GSSCredElement) myCred,
lifetime, cStub);
}
public GSSContextSpi getMechanismContext(GSSCredentialSpi myCred)
throws GSSException {
if (myCred == null) {
myCred = getCredFromSubject(null, false);
} else if (!(myCred instanceof GSSCredElement)) {
throw new GSSException(GSSException.NO_CRED);
}
return new NativeGSSContext((GSSCredElement) myCred, cStub);
}
public GSSContextSpi getMechanismContext(byte[] exportedContext)
throws GSSException {
return cStub.importContext(exportedContext);
}
public final Oid getMechanismOid() {
return cStub.getMech();
}
public Provider getProvider() {
return SunNativeProvider.INSTANCE;
}
public Oid[] getNameTypes() throws GSSException {
return cStub.inquireNamesForMech();
}
}

View file

@ -0,0 +1,130 @@
/*
* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.wrapper;
import java.util.HashMap;
import java.security.Provider;
import java.security.AccessController;
import java.security.PrivilegedAction;
import org.ietf.jgss.Oid;
import sun.security.action.PutAllAction;
import static sun.security.util.SecurityConstants.PROVIDER_VER;
/**
* Defines the Sun NativeGSS provider for plugging in the
* native GSS mechanisms to Java GSS.
*
* List of supported mechanisms depends on the local
* machine configuration.
*
* @author Yu-Ching Valerie Peng
*/
public final class SunNativeProvider extends Provider {
private static final long serialVersionUID = -238911724858694204L;
private static final String NAME = "SunNativeGSS";
private static final String INFO = "Sun Native GSS provider";
private static final String MF_CLASS =
"sun.security.jgss.wrapper.NativeGSSFactory";
private static final String LIB_PROP = "sun.security.jgss.lib";
private static final String DEBUG_PROP = "sun.security.nativegss.debug";
private static HashMap<String, String> MECH_MAP;
static final Provider INSTANCE = new SunNativeProvider();
static boolean DEBUG;
static void debug(String message) {
if (DEBUG) {
if (message == null) {
throw new NullPointerException();
}
System.out.println(NAME + ": " + message);
}
}
static {
MECH_MAP =
AccessController.doPrivileged(
new PrivilegedAction<HashMap<String, String>>() {
public HashMap<String, String> run() {
DEBUG = Boolean.parseBoolean
(System.getProperty(DEBUG_PROP));
try {
System.loadLibrary("j2gss");
} catch (Error err) {
debug("No j2gss library found!");
if (DEBUG) err.printStackTrace();
return null;
}
String[] gssLibs = new String[0];
String defaultLib = System.getProperty(LIB_PROP);
if (defaultLib == null || defaultLib.trim().equals("")) {
String osname = System.getProperty("os.name");
if (osname.startsWith("SunOS")) {
gssLibs = new String[]{ "libgss.so" };
} else if (osname.startsWith("Linux")) {
gssLibs = new String[]{
"libgssapi.so",
"libgssapi_krb5.so",
"libgssapi_krb5.so.2",
};
} else if (osname.contains("OS X")) {
gssLibs = new String[]{
"libgssapi_krb5.dylib",
"/usr/lib/sasl2/libgssapiv2.2.so",
};
}
} else {
gssLibs = new String[]{ defaultLib };
}
for (String libName: gssLibs) {
if (GSSLibStub.init(libName, DEBUG)) {
debug("Loaded GSS library: " + libName);
Oid[] mechs = GSSLibStub.indicateMechs();
HashMap<String, String> map =
new HashMap<String, String>();
for (int i = 0; i < mechs.length; i++) {
debug("Native MF for " + mechs[i]);
map.put("GssApiMechanism." + mechs[i],
MF_CLASS);
}
return map;
}
}
return null;
}
});
}
public SunNativeProvider() {
/* We are the Sun NativeGSS provider */
super(NAME, PROVIDER_VER, INFO);
if (MECH_MAP != null) {
AccessController.doPrivileged(new PutAllAction(this, MECH_MAP));
}
}
}

View file

@ -0,0 +1,41 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
public class Asn1Exception extends KrbException {
private static final long serialVersionUID = 8291288984575084132L;
public Asn1Exception(int i) {
super(i);
}
}

View file

@ -0,0 +1,343 @@
/*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import java.util.Arrays;
import sun.security.util.*;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
import java.io.IOException;
import java.math.BigInteger;
/**
* This class encapsulates the concept of a Kerberos checksum.
*/
public class Checksum {
private int cksumType;
private byte[] checksum;
// ----------------------------------------------+-------------+-----------
// Checksum type |sumtype |checksum
// |value | size
// ----------------------------------------------+-------------+-----------
public static final int CKSUMTYPE_NULL = 0; // 0
public static final int CKSUMTYPE_CRC32 = 1; // 4
public static final int CKSUMTYPE_RSA_MD4 = 2; // 16
public static final int CKSUMTYPE_RSA_MD4_DES = 3; // 24
public static final int CKSUMTYPE_DES_MAC = 4; // 16
public static final int CKSUMTYPE_DES_MAC_K = 5; // 8
public static final int CKSUMTYPE_RSA_MD4_DES_K = 6; // 16
public static final int CKSUMTYPE_RSA_MD5 = 7; // 16
public static final int CKSUMTYPE_RSA_MD5_DES = 8; // 24
// draft-ietf-krb-wg-crypto-07.txt
public static final int CKSUMTYPE_HMAC_SHA1_DES3_KD = 12; // 20
// draft-raeburn-krb-rijndael-krb-07.txt
public static final int CKSUMTYPE_HMAC_SHA1_96_AES128 = 15; // 96
public static final int CKSUMTYPE_HMAC_SHA1_96_AES256 = 16; // 96
// draft-brezak-win2k-krb-rc4-hmac-04.txt
public static final int CKSUMTYPE_HMAC_MD5_ARCFOUR = -138;
static int CKSUMTYPE_DEFAULT;
static int SAFECKSUMTYPE_DEFAULT;
private static boolean DEBUG = Krb5.DEBUG;
static {
initStatic();
}
public static void initStatic() {
String temp = null;
Config cfg = null;
try {
cfg = Config.getInstance();
temp = cfg.get("libdefaults", "default_checksum");
if (temp != null)
{
CKSUMTYPE_DEFAULT = Config.getType(temp);
} else {
/*
* If the default checksum is not
* specified in the configuration we
* set it to RSA_MD5. We follow the MIT and
* SEAM implementation.
*/
CKSUMTYPE_DEFAULT = CKSUMTYPE_RSA_MD5;
}
} catch (Exception exc) {
if (DEBUG) {
System.out.println("Exception in getting default checksum "+
"value from the configuration " +
"Setting default checksum to be RSA-MD5");
exc.printStackTrace();
}
CKSUMTYPE_DEFAULT = CKSUMTYPE_RSA_MD5;
}
try {
temp = cfg.get("libdefaults", "safe_checksum_type");
if (temp != null)
{
SAFECKSUMTYPE_DEFAULT = Config.getType(temp);
} else {
SAFECKSUMTYPE_DEFAULT = CKSUMTYPE_RSA_MD5_DES;
}
} catch (Exception exc) {
if (DEBUG) {
System.out.println("Exception in getting safe default " +
"checksum value " +
"from the configuration Setting " +
"safe default checksum to be RSA-MD5");
exc.printStackTrace();
}
SAFECKSUMTYPE_DEFAULT = CKSUMTYPE_RSA_MD5_DES;
}
}
/**
* Constructs a new Checksum using the raw data and type.
* @param data the byte array of checksum.
* @param new_cksumType the type of checksum.
*
*/
// used in InitialToken
public Checksum(byte[] data, int new_cksumType) {
cksumType = new_cksumType;
checksum = data;
}
/**
* Constructs a new Checksum by calculating the checksum over the data
* using specified checksum type.
* @param new_cksumType the type of checksum.
* @param data the data that needs to be performed a checksum calculation on.
*/
public Checksum(int new_cksumType, byte[] data)
throws KdcErrException, KrbCryptoException {
cksumType = new_cksumType;
CksumType cksumEngine = CksumType.getInstance(cksumType);
if (!cksumEngine.isSafe()) {
checksum = cksumEngine.calculateChecksum(data, data.length);
} else {
throw new KdcErrException(Krb5.KRB_AP_ERR_INAPP_CKSUM);
}
}
/**
* Constructs a new Checksum by calculating the keyed checksum
* over the data using specified checksum type.
* @param new_cksumType the type of checksum.
* @param data the data that needs to be performed a checksum calculation on.
*/
// KrbSafe, KrbTgsReq
public Checksum(int new_cksumType, byte[] data,
EncryptionKey key, int usage)
throws KdcErrException, KrbApErrException, KrbCryptoException {
cksumType = new_cksumType;
CksumType cksumEngine = CksumType.getInstance(cksumType);
if (!cksumEngine.isSafe())
throw new KrbApErrException(Krb5.KRB_AP_ERR_INAPP_CKSUM);
checksum =
cksumEngine.calculateKeyedChecksum(data,
data.length,
key.getBytes(),
usage);
}
/**
* Verifies the keyed checksum over the data passed in.
*/
public boolean verifyKeyedChecksum(byte[] data, EncryptionKey key,
int usage)
throws KdcErrException, KrbApErrException, KrbCryptoException {
CksumType cksumEngine = CksumType.getInstance(cksumType);
if (!cksumEngine.isSafe())
throw new KrbApErrException(Krb5.KRB_AP_ERR_INAPP_CKSUM);
return cksumEngine.verifyKeyedChecksum(data,
data.length,
key.getBytes(),
checksum,
usage);
}
/*
public Checksum(byte[] data) throws KdcErrException, KrbCryptoException {
this(Checksum.CKSUMTYPE_DEFAULT, data);
}
*/
boolean isEqual(Checksum cksum) throws KdcErrException {
if (cksumType != cksum.cksumType)
return false;
CksumType cksumEngine = CksumType.getInstance(cksumType);
return CksumType.isChecksumEqual(checksum, cksum.checksum);
}
/**
* Constructs an instance of Checksum from an ASN.1 encoded representation.
* @param encoding a single DER-encoded value.
* @exception Asn1Exception if an error occurs while decoding an ASN1
* encoded data.
* @exception IOException if an I/O error occurs while reading encoded data.
*
*/
private Checksum(DerValue encoding) throws Asn1Exception, IOException {
DerValue der;
if (encoding.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte)0x1F) == (byte)0x00) {
cksumType = der.getData().getBigInteger().intValue();
}
else
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte)0x1F) == (byte)0x01) {
checksum = der.getData().getOctetString();
}
else
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
if (encoding.getData().available() > 0) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
}
/**
* Encodes a Checksum object.
* <pre>{@code
* Checksum ::= SEQUENCE {
* cksumtype [0] Int32,
* checksum [1] OCTET STRING
* }
* }</pre>
*
* <p>
* This definition reflects the Network Working Group RFC 4120
* specification available at
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
* @return byte array of enocded Checksum.
* @exception Asn1Exception if an error occurs while decoding an
* ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading
* encoded data.
*
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
DerOutputStream temp = new DerOutputStream();
temp.putInteger(BigInteger.valueOf(cksumType));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte)0x00), temp);
temp = new DerOutputStream();
temp.putOctetString(checksum);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte)0x01), temp);
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
return temp.toByteArray();
}
/**
* Parse (unmarshal) a checksum object from a DER input stream. This form
* parsing might be used when expanding a value which is part of
* a constructed sequence and uses explicitly tagged type.
*
* @exception Asn1Exception if an error occurs while decoding an
* ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading
* encoded data.
* @param data the Der input stream value, which contains one or more
* marshaled value.
* @param explicitTag tag number.
* @param optional indicates if this data field is optional
* @return an instance of Checksum.
*
*/
public static Checksum parse(DerInputStream data,
byte explicitTag, boolean optional)
throws Asn1Exception, IOException {
if ((optional) &&
(((byte)data.peekByte() & (byte)0x1F) != explicitTag)) {
return null;
}
DerValue der = data.getDerValue();
if (explicitTag != (der.getTag() & (byte)0x1F)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
} else {
DerValue subDer = der.getData().getDerValue();
return new Checksum(subDer);
}
}
/**
* Returns the raw bytes of the checksum, not in ASN.1 encoded form.
*/
public final byte[] getBytes() {
return checksum;
}
public final int getType() {
return cksumType;
}
@Override public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Checksum)) {
return false;
}
try {
return isEqual((Checksum)obj);
} catch (KdcErrException kee) {
return false;
}
}
@Override public int hashCode() {
int result = 17;
result = 37 * result + cksumType;
if (checksum != null) {
result = 37 * result + Arrays.hashCode(checksum);
}
return result;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2001, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import java.security.SecureRandom;
public final class Confounder {
private static SecureRandom srand = new SecureRandom();
private Confounder() { // not instantiable
}
public static byte[] bytes(int size) {
byte[] data = new byte[size];
srand.nextBytes(data);
return data;
}
public static int intValue() {
return srand.nextInt();
}
public static long longValue() {
return srand.nextLong();
}
}

View file

@ -0,0 +1,549 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.ccache.CredentialsCache;
import sun.security.krb5.internal.crypto.EType;
import java.io.IOException;
import java.util.Date;
import java.util.Locale;
import java.net.InetAddress;
/**
* This class encapsulates the concept of a Kerberos service
* credential. That includes a Kerberos ticket and an associated
* session key.
*/
public class Credentials {
Ticket ticket;
PrincipalName client;
PrincipalName server;
EncryptionKey key;
TicketFlags flags;
KerberosTime authTime;
KerberosTime startTime;
KerberosTime endTime;
KerberosTime renewTill;
HostAddresses cAddr;
EncryptionKey serviceKey;
AuthorizationData authzData;
private static boolean DEBUG = Krb5.DEBUG;
private static CredentialsCache cache;
static boolean alreadyLoaded = false;
private static boolean alreadyTried = false;
// Read native ticket with session key type in the given list
private static native Credentials acquireDefaultNativeCreds(int[] eTypes);
public Credentials(Ticket new_ticket,
PrincipalName new_client,
PrincipalName new_server,
EncryptionKey new_key,
TicketFlags new_flags,
KerberosTime authTime,
KerberosTime new_startTime,
KerberosTime new_endTime,
KerberosTime renewTill,
HostAddresses cAddr,
AuthorizationData authzData) {
this(new_ticket, new_client, new_server, new_key, new_flags,
authTime, new_startTime, new_endTime, renewTill, cAddr);
this.authzData = authzData;
}
public Credentials(Ticket new_ticket,
PrincipalName new_client,
PrincipalName new_server,
EncryptionKey new_key,
TicketFlags new_flags,
KerberosTime authTime,
KerberosTime new_startTime,
KerberosTime new_endTime,
KerberosTime renewTill,
HostAddresses cAddr) {
ticket = new_ticket;
client = new_client;
server = new_server;
key = new_key;
flags = new_flags;
this.authTime = authTime;
startTime = new_startTime;
endTime = new_endTime;
this.renewTill = renewTill;
this.cAddr = cAddr;
}
public Credentials(byte[] encoding,
String client,
String server,
byte[] keyBytes,
int keyType,
boolean[] flags,
Date authTime,
Date startTime,
Date endTime,
Date renewTill,
InetAddress[] cAddrs) throws KrbException, IOException {
this(new Ticket(encoding),
new PrincipalName(client, PrincipalName.KRB_NT_PRINCIPAL),
new PrincipalName(server, PrincipalName.KRB_NT_SRV_INST),
new EncryptionKey(keyType, keyBytes),
(flags == null? null: new TicketFlags(flags)),
(authTime == null? null: new KerberosTime(authTime)),
(startTime == null? null: new KerberosTime(startTime)),
(endTime == null? null: new KerberosTime(endTime)),
(renewTill == null? null: new KerberosTime(renewTill)),
null); // caddrs are in the encoding at this point
}
/**
* Acquires a service ticket for the specified service
* principal. If the service ticket is not already available, it
* obtains a new one from the KDC.
*/
/*
public Credentials(Credentials tgt, PrincipalName service)
throws KrbException {
}
*/
public final PrincipalName getClient() {
return client;
}
public final PrincipalName getServer() {
return server;
}
public final EncryptionKey getSessionKey() {
return key;
}
public final Date getAuthTime() {
if (authTime != null) {
return authTime.toDate();
} else {
return null;
}
}
public final Date getStartTime() {
if (startTime != null)
{
return startTime.toDate();
}
return null;
}
public final Date getEndTime() {
if (endTime != null)
{
return endTime.toDate();
}
return null;
}
public final Date getRenewTill() {
if (renewTill != null)
{
return renewTill.toDate();
}
return null;
}
public final boolean[] getFlags() {
if (flags == null) // Can be in a KRB-CRED
return null;
return flags.toBooleanArray();
}
public final InetAddress[] getClientAddresses() {
if (cAddr == null)
return null;
return cAddr.getInetAddresses();
}
public final byte[] getEncoded() {
byte[] retVal = null;
try {
retVal = ticket.asn1Encode();
} catch (Asn1Exception e) {
if (DEBUG)
System.out.println(e);
} catch (IOException ioe) {
if (DEBUG)
System.out.println(ioe);
}
return retVal;
}
public boolean isForwardable() {
return flags.get(Krb5.TKT_OPTS_FORWARDABLE);
}
public boolean isRenewable() {
return flags.get(Krb5.TKT_OPTS_RENEWABLE);
}
public Ticket getTicket() {
return ticket;
}
public TicketFlags getTicketFlags() {
return flags;
}
public AuthorizationData getAuthzData() {
return authzData;
}
/**
* Checks if the service ticket returned by the KDC has the OK-AS-DELEGATE
* flag set
* @return true if OK-AS_DELEGATE flag is set, otherwise, return false.
*/
public boolean checkDelegate() {
return flags.get(Krb5.TKT_OPTS_DELEGATE);
}
/**
* Reset TKT_OPTS_DELEGATE to false, called at credentials acquirement
* when one of the cross-realm TGTs does not have the OK-AS-DELEGATE
* flag set. This info must be preservable and restorable through
* the Krb5Util.credsToTicket/ticketToCreds() methods so that even if
* the service ticket is cached it still remembers the cross-realm
* authentication result.
*/
public void resetDelegate() {
flags.set(Krb5.TKT_OPTS_DELEGATE, false);
}
public Credentials renew() throws KrbException, IOException {
KDCOptions options = new KDCOptions();
options.set(KDCOptions.RENEW, true);
/*
* Added here to pass KrbKdcRep.check:73
*/
options.set(KDCOptions.RENEWABLE, true);
return new KrbTgsReq(options,
this,
server,
null, // from
null, // till
null, // rtime
null, // eTypes
cAddr,
null,
null,
null).sendAndGetCreds();
}
/**
* Returns a TGT for the given client principal from a ticket cache.
*
* @param princ the client principal. A value of null means that the
* default principal name in the credentials cache will be used.
* @param ticketCache the path to the tickets file. A value
* of null will be accepted to indicate that the default
* path should be searched
* @return the TGT credentials or null if none were found. If the tgt
* expired, it is the responsibility of the caller to determine this.
*/
public static Credentials acquireTGTFromCache(PrincipalName princ,
String ticketCache)
throws KrbException, IOException {
if (ticketCache == null) {
// The default ticket cache on Windows and Mac is not a file.
String os = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("os.name"));
if (os.toUpperCase(Locale.ENGLISH).startsWith("WINDOWS") ||
os.toUpperCase(Locale.ENGLISH).contains("OS X")) {
Credentials creds = acquireDefaultCreds();
if (creds == null) {
if (DEBUG) {
System.out.println(">>> Found no TGT's in LSA");
}
return null;
}
if (princ != null) {
if (creds.getClient().equals(princ)) {
if (DEBUG) {
System.out.println(">>> Obtained TGT from LSA: "
+ creds);
}
return creds;
} else {
if (DEBUG) {
System.out.println(">>> LSA contains TGT for "
+ creds.getClient()
+ " not "
+ princ);
}
return null;
}
} else {
if (DEBUG) {
System.out.println(">>> Obtained TGT from LSA: "
+ creds);
}
return creds;
}
}
}
/*
* Returns the appropriate cache. If ticketCache is null, it is the
* default cache otherwise it is the cache filename contained in it.
*/
CredentialsCache ccache =
CredentialsCache.getInstance(princ, ticketCache);
if (ccache == null) {
return null;
}
sun.security.krb5.internal.ccache.Credentials tgtCred =
ccache.getDefaultCreds();
if (tgtCred == null) {
return null;
}
if (EType.isSupported(tgtCred.getEType())) {
return tgtCred.setKrbCreds();
} else {
if (DEBUG) {
System.out.println(
">>> unsupported key type found the default TGT: " +
tgtCred.getEType());
}
return null;
}
}
/**
* Acquires default credentials.
* <br>The possible locations for default credentials cache is searched in
* the following order:
* <ol>
* <li> The directory and cache file name specified by "KRB5CCNAME" system.
* property.
* <li> The directory and cache file name specified by "KRB5CCNAME"
* environment variable.
* <li> A cache file named krb5cc_{user.name} at {user.home} directory.
* </ol>
* @return a <code>KrbCreds</code> object if the credential is found,
* otherwise return null.
*/
// this method is intentionally changed to not check if the caller's
// principal name matches cache file's principal name.
// It assumes that the GSS call has
// the privilege to access the default cache file.
// This method is only called on Windows and Mac OS X, the native
// acquireDefaultNativeCreds is also available on these platforms.
public static synchronized Credentials acquireDefaultCreds() {
Credentials result = null;
if (cache == null) {
cache = CredentialsCache.getInstance();
}
if (cache != null) {
sun.security.krb5.internal.ccache.Credentials temp =
cache.getDefaultCreds();
if (temp != null) {
if (DEBUG) {
System.out.println(">>> KrbCreds found the default ticket"
+ " granting ticket in credential cache.");
}
if (EType.isSupported(temp.getEType())) {
result = temp.setKrbCreds();
} else {
if (DEBUG) {
System.out.println(
">>> unsupported key type found the default TGT: " +
temp.getEType());
}
}
}
}
if (result == null) {
// Doesn't seem to be a default cache on this system or
// TGT has unsupported encryption type
if (!alreadyTried) {
// See if there's any native code to load
try {
ensureLoaded();
} catch (Exception e) {
if (DEBUG) {
System.out.println("Can not load credentials cache");
e.printStackTrace();
}
alreadyTried = true;
}
}
if (alreadyLoaded) {
// There is some native code
if (DEBUG) {
System.out.println(">> Acquire default native Credentials");
}
try {
result = acquireDefaultNativeCreds(
EType.getDefaults("default_tkt_enctypes"));
} catch (KrbException ke) {
// when there is no default_tkt_enctypes.
}
}
}
return result;
}
/**
* Acquires credentials for a specified service using initial credential.
* When the service has a different realm
* from the initial credential, we do cross-realm authentication
* - first, we use the current credential to get
* a cross-realm credential from the local KDC, then use that
* cross-realm credential to request service credential
* from the foreigh KDC.
*
* @param service the name of service principal using format
* components@realm
* @param ccreds client's initial credential.
* @exception IOException if an error occurs in reading the credentials
* cache
* @exception KrbException if an error occurs specific to Kerberos
* @return a <code>Credentials</code> object.
*/
public static Credentials acquireServiceCreds(String service,
Credentials ccreds)
throws KrbException, IOException {
return CredentialsUtil.acquireServiceCreds(service, ccreds);
}
public static Credentials acquireS4U2selfCreds(PrincipalName user,
Credentials ccreds) throws KrbException, IOException {
return CredentialsUtil.acquireS4U2selfCreds(user, ccreds);
}
public static Credentials acquireS4U2proxyCreds(String service,
Ticket second, PrincipalName client, Credentials ccreds)
throws KrbException, IOException {
return CredentialsUtil.acquireS4U2proxyCreds(
service, second, client, ccreds);
}
public CredentialsCache getCache() {
return cache;
}
public EncryptionKey getServiceKey() {
return serviceKey;
}
/*
* Prints out debug info.
*/
public static void printDebug(Credentials c) {
System.out.println(">>> DEBUG: ----Credentials----");
System.out.println("\tclient: " + c.client.toString());
System.out.println("\tserver: " + c.server.toString());
System.out.println("\tticket: sname: " + c.ticket.sname.toString());
if (c.startTime != null) {
System.out.println("\tstartTime: " + c.startTime.getTime());
}
System.out.println("\tendTime: " + c.endTime.getTime());
System.out.println(" ----Credentials end----");
}
static void ensureLoaded() {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void> () {
public Void run() {
if (System.getProperty("os.name").contains("OS X")) {
System.loadLibrary("osxkrb5");
} else {
System.loadLibrary("w2k_lsa_auth");
}
return null;
}
});
alreadyLoaded = true;
}
public String toString() {
StringBuilder sb = new StringBuilder("Credentials:");
sb.append( "\n client=").append(client);
sb.append( "\n server=").append(server);
if (authTime != null) {
sb.append("\n authTime=").append(authTime);
}
if (startTime != null) {
sb.append("\n startTime=").append(startTime);
}
sb.append( "\n endTime=").append(endTime);
sb.append( "\n renewTill=").append(renewTill);
sb.append( "\n flags=").append(flags);
sb.append( "\nEType (skey)=").append(key.getEType());
sb.append( "\n (tkt key)=").append(ticket.encPart.eType);
return sb.toString();
}
public sun.security.krb5.internal.ccache.Credentials toCCacheCreds() {
return new sun.security.krb5.internal.ccache.Credentials(
getClient(), getServer(),
getSessionKey(),
date2kt(getAuthTime()),
date2kt(getStartTime()),
date2kt(getEndTime()),
date2kt(getRenewTill()),
false,
flags,
new HostAddresses(getClientAddresses()),
getAuthzData(),
getTicket(),
null);
}
private static KerberosTime date2kt(Date d) {
return d == null ? null : new KerberosTime(d);
}
}

View file

@ -0,0 +1,380 @@
/*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.util.*;
import sun.security.krb5.internal.crypto.*;
import sun.security.krb5.internal.*;
import java.io.IOException;
import java.math.BigInteger;
/**
* This class encapsulates Kerberos encrypted data. It allows
* callers access to both the ASN.1 encoded form of the EncryptedData
* type as well as the raw cipher text.
*/
public class EncryptedData implements Cloneable {
int eType;
Integer kvno; // optional
byte[] cipher;
byte[] plain; // not part of ASN.1 encoding
// ----------------+-----------+----------+----------------+---------------
// Encryption type |etype value|block size|minimum pad size|confounder size
// ----------------+-----------+----------+----------------+---------------
public static final int
ETYPE_NULL = 0; // 1 0 0
public static final int
ETYPE_DES_CBC_CRC = 1; // 8 4 8
public static final int
ETYPE_DES_CBC_MD4 = 2; // 8 0 8
public static final int
ETYPE_DES_CBC_MD5 = 3; // 8 0 8
// draft-brezak-win2k-krb-rc4-hmac-04.txt
public static final int
ETYPE_ARCFOUR_HMAC = 23; // 1
// NOTE: the exportable RC4-HMAC is not supported;
// it is no longer a usable encryption type
public static final int
ETYPE_ARCFOUR_HMAC_EXP = 24; // 1
// draft-ietf-krb-wg-crypto-07.txt
public static final int
ETYPE_DES3_CBC_HMAC_SHA1_KD = 16; // 8 0 8
// draft-raeburn-krb-rijndael-krb-07.txt
public static final int
ETYPE_AES128_CTS_HMAC_SHA1_96 = 17; // 16 0 16
public static final int
ETYPE_AES256_CTS_HMAC_SHA1_96 = 18; // 16 0 16
/* used by self */
private EncryptedData() {
}
public Object clone() {
EncryptedData new_encryptedData = new EncryptedData();
new_encryptedData.eType = eType;
if (kvno != null) {
new_encryptedData.kvno = kvno.intValue();
}
if (cipher != null) {
new_encryptedData.cipher = new byte[cipher.length];
System.arraycopy(cipher, 0, new_encryptedData.cipher,
0, cipher.length);
}
return new_encryptedData;
}
// Used in JSSE (com.sun.net.ssl.internal.KerberosPreMasterSecret)
public EncryptedData(
int new_eType,
Integer new_kvno,
byte[] new_cipher) {
eType = new_eType;
kvno = new_kvno;
cipher = new_cipher;
}
/*
// Not used.
public EncryptedData(
EncryptionKey key,
byte[] plaintext)
throws KdcErrException, KrbCryptoException {
EType etypeEngine = EType.getInstance(key.getEType());
cipher = etypeEngine.encrypt(plaintext, key.getBytes());
eType = key.getEType();
kvno = key.getKeyVersionNumber();
}
*/
// used in KrbApRep, KrbApReq, KrbAsReq, KrbCred, KrbPriv
// Used in JSSE (com.sun.net.ssl.internal.KerberosPreMasterSecret)
public EncryptedData(
EncryptionKey key,
byte[] plaintext,
int usage)
throws KdcErrException, KrbCryptoException {
EType etypeEngine = EType.getInstance(key.getEType());
cipher = etypeEngine.encrypt(plaintext, key.getBytes(), usage);
eType = key.getEType();
kvno = key.getKeyVersionNumber();
}
/*
// Not used.
public EncryptedData(
EncryptionKey key,
byte[] ivec,
byte[] plaintext)
throws KdcErrException, KrbCryptoException {
EType etypeEngine = EType.getInstance(key.getEType());
cipher = etypeEngine.encrypt(plaintext, key.getBytes(), ivec);
eType = key.getEType();
kvno = key.getKeyVersionNumber();
}
*/
/*
// Not used.
EncryptedData(
StringBuffer password,
byte[] plaintext)
throws KdcErrException, KrbCryptoException {
EncryptionKey key = new EncryptionKey(password);
EType etypeEngine = EType.getInstance(key.getEType());
cipher = etypeEngine.encrypt(plaintext, key.getBytes());
eType = key.getEType();
kvno = key.getKeyVersionNumber();
}
*/
public byte[] decrypt(
EncryptionKey key, int usage)
throws KdcErrException, KrbApErrException, KrbCryptoException {
if (eType != key.getEType()) {
throw new KrbCryptoException(
"EncryptedData is encrypted using keytype " +
EType.toString(eType) +
" but decryption key is of type " +
EType.toString(key.getEType()));
}
EType etypeEngine = EType.getInstance(eType);
plain = etypeEngine.decrypt(cipher, key.getBytes(), usage);
// The service ticket will be used in S4U2proxy request. Therefore
// the raw ticket is still needed.
//cipher = null;
return etypeEngine.decryptedData(plain);
}
/*
// currently destructive on cipher
// Not used.
public byte[] decrypt(
EncryptionKey key,
byte[] ivec, int usage)
throws KdcErrException, KrbApErrException, KrbCryptoException {
// XXX check for matching eType and kvno here
EType etypeEngine = EType.getInstance(eType);
plain = etypeEngine.decrypt(cipher, key.getBytes(), ivec, usage);
cipher = null;
return etypeEngine.decryptedData(plain);
}
// currently destructive on cipher
// Not used.
byte[] decrypt(StringBuffer password)
throws KdcErrException, KrbApErrException, KrbCryptoException {
EncryptionKey key = new EncryptionKey(password);
// XXX check for matching eType here
EType etypeEngine = EType.getInstance(eType);
plain = etypeEngine.decrypt(cipher, key.getBytes());
cipher = null;
return etypeEngine.decryptedData(plain);
}
*/
private byte[] decryptedData() throws KdcErrException {
if (plain != null) {
EType etypeEngine = EType.getInstance(eType);
return etypeEngine.decryptedData(plain);
}
return null;
}
/**
* Constructs an instance of EncryptedData type.
* @param encoding a single DER-encoded value.
* @exception Asn1Exception if an error occurs while decoding an
* ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading encoded
* data.
*
*/
/* Used by self */
private EncryptedData(DerValue encoding)
throws Asn1Exception, IOException {
DerValue der = null;
if (encoding.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte)0x1F) == (byte)0x00) {
eType = (der.getData().getBigInteger()).intValue();
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
if ((encoding.getData().peekByte() & 0x1F) == 1) {
der = encoding.getData().getDerValue();
int i = (der.getData().getBigInteger()).intValue();
kvno = i;
} else {
kvno = null;
}
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte)0x1F) == (byte)0x02) {
cipher = der.getData().getOctetString();
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
if (encoding.getData().available() > 0) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
}
/**
* Returns an ASN.1 encoded EncryptedData type.
*
* <pre>{@code
* EncryptedData ::= SEQUENCE {
* etype [0] Int32 -- EncryptionType --,
* kvno [1] UInt32 OPTIONAL,
* cipher [2] OCTET STRING -- ciphertext
* }
* }</pre>
*
* <p>
* This definition reflects the Network Working Group RFC 4120
* specification available at
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
*
* @return byte array of encoded EncryptedData object.
* @exception Asn1Exception if an error occurs while decoding an
* ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading
* encoded data.
*
*/
public byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
DerOutputStream temp = new DerOutputStream();
temp.putInteger(BigInteger.valueOf(this.eType));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte)0x00), temp);
temp = new DerOutputStream();
if (kvno != null) {
// encode as an unsigned integer (UInt32)
temp.putInteger(BigInteger.valueOf(this.kvno.longValue()));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte)0x01), temp);
temp = new DerOutputStream();
}
temp.putOctetString(this.cipher);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
(byte)0x02), temp);
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
return temp.toByteArray();
}
/**
* Parse (unmarshal) an EncryptedData from a DER input stream. This form
* parsing might be used when expanding a value which is part of
* a constructed sequence and uses explicitly tagged type.
*
* @param data the Der input stream value, which contains one or more
* marshaled value.
* @param explicitTag tag number.
* @param optional indicate if this data field is optional
* @exception Asn1Exception if an error occurs while decoding an
* ASN1 encoded data.
* @exception IOException if an I/O error occurs while reading
* encoded data.
* @return an instance of EncryptedData.
*
*/
public static EncryptedData parse(DerInputStream data,
byte explicitTag,
boolean optional)
throws Asn1Exception, IOException {
if ((optional) &&
(((byte)data.peekByte() & (byte)0x1F) != explicitTag))
return null;
DerValue der = data.getDerValue();
if (explicitTag != (der.getTag() & (byte)0x1F)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
} else {
DerValue subDer = der.getData().getDerValue();
return new EncryptedData(subDer);
}
}
/**
* Reset asn.1 data stream after decryption, remove redundant bytes.
* @param data the decrypted data from decrypt().
* @return the reset byte array which holds exactly one asn1 datum
* including its tag and length.
*
*/
public byte[] reset(byte[] data) {
byte[] bytes = null;
// for asn.1 encoded data, we use length field to
// determine the data length and remove redundant paddings.
if ((data[1] & 0xFF) < 128) {
bytes = new byte[data[1] + 2];
System.arraycopy(data, 0, bytes, 0, data[1] + 2);
} else {
if ((data[1] & 0xFF) > 128) {
int len = data[1] & (byte)0x7F;
int result = 0;
for (int i = 0; i < len; i++) {
result |= (data[i + 2] & 0xFF) << (8 * (len - i - 1));
}
bytes = new byte[result + len + 2];
System.arraycopy(data, 0, bytes, 0, result + len + 2);
}
}
return bytes;
}
public int getEType() {
return eType;
}
public Integer getKeyVersionNumber() {
return kvno;
}
/**
* Returns the raw cipher text bytes, not in ASN.1 encoding.
*/
public byte[] getBytes() {
return cipher;
}
}

View file

@ -0,0 +1,583 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.util.*;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import sun.security.krb5.internal.ktab.KeyTab;
import sun.security.krb5.internal.ccache.CCacheOutputStream;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.DESedeKeySpec;
/**
* This class encapsulates the concept of an EncryptionKey. An encryption
* key is defined in RFC 4120 as:
*
* EncryptionKey ::= SEQUENCE {
* keytype [0] Int32 -- actually encryption type --,
* keyvalue [1] OCTET STRING
* }
*
* keytype
* This field specifies the encryption type of the encryption key
* that follows in the keyvalue field. Although its name is
* "keytype", it actually specifies an encryption type. Previously,
* multiple cryptosystems that performed encryption differently but
* were capable of using keys with the same characteristics were
* permitted to share an assigned number to designate the type of
* key; this usage is now deprecated.
*
* keyvalue
* This field contains the key itself, encoded as an octet string.
*/
public class EncryptionKey
implements Cloneable {
public static final EncryptionKey NULL_KEY =
new EncryptionKey(new byte[] {}, EncryptedData.ETYPE_NULL, null);
private int keyType;
private byte[] keyValue;
private Integer kvno; // not part of ASN1 encoding;
private static final boolean DEBUG = Krb5.DEBUG;
public synchronized int getEType() {
return keyType;
}
public final Integer getKeyVersionNumber() {
return kvno;
}
/**
* Returns the raw key bytes, not in any ASN.1 encoding.
*/
public final byte[] getBytes() {
// This method cannot be called outside sun.security, hence no
// cloning. getEncoded() calls this method.
return keyValue;
}
public synchronized Object clone() {
return new EncryptionKey(keyValue, keyType, kvno);
}
/**
* Obtains all versions of the secret key of the principal from a
* keytab.
*
* @param princ the principal whose secret key is desired
* @param keytab the path to the keytab file. A value of null
* will be accepted to indicate that the default path should be
* searched.
* @return an array of secret keys or null if none were found.
*/
public static EncryptionKey[] acquireSecretKeys(PrincipalName princ,
String keytab) {
if (princ == null)
throw new IllegalArgumentException(
"Cannot have null pricipal name to look in keytab.");
// KeyTab getInstance(keytab) will call KeyTab.getInstance()
// if keytab is null
KeyTab ktab = KeyTab.getInstance(keytab);
return ktab.readServiceKeys(princ);
}
/**
* Obtains a key for a given etype of a principal with possible new salt
* and s2kparams
* @param cname NOT null
* @param password NOT null
* @param etype
* @param snp can be NULL
* @return never null
*/
public static EncryptionKey acquireSecretKey(PrincipalName cname,
char[] password, int etype, PAData.SaltAndParams snp)
throws KrbException {
String salt;
byte[] s2kparams;
if (snp != null) {
salt = snp.salt != null ? snp.salt : cname.getSalt();
s2kparams = snp.params;
} else {
salt = cname.getSalt();
s2kparams = null;
}
return acquireSecretKey(password, salt, etype, s2kparams);
}
/**
* Obtains a key for a given etype with salt and optional s2kparams
* @param password NOT null
* @param salt NOT null
* @param etype
* @param s2kparams can be NULL
* @return never null
*/
public static EncryptionKey acquireSecretKey(char[] password,
String salt, int etype, byte[] s2kparams)
throws KrbException {
return new EncryptionKey(
stringToKey(password, salt, s2kparams, etype),
etype, null);
}
/**
* Generate a list of keys using the given principal and password.
* Construct a key for each configured etype.
* Caller is responsible for clearing password.
*/
/*
* Usually, when keyType is decoded from ASN.1 it will contain a
* value indicating what the algorithm to be used is. However, when
* converting from a password to a key for the AS-EXCHANGE, this
* keyType will not be available. Use builtin list of default etypes
* as the default in that case. If default_tkt_enctypes was set in
* the libdefaults of krb5.conf, then use that sequence.
*/
public static EncryptionKey[] acquireSecretKeys(char[] password,
String salt) throws KrbException {
int[] etypes = EType.getDefaults("default_tkt_enctypes");
EncryptionKey[] encKeys = new EncryptionKey[etypes.length];
for (int i = 0; i < etypes.length; i++) {
if (EType.isSupported(etypes[i])) {
encKeys[i] = new EncryptionKey(
stringToKey(password, salt, null, etypes[i]),
etypes[i], null);
} else {
if (DEBUG) {
System.out.println("Encryption Type " +
EType.toString(etypes[i]) +
" is not supported/enabled");
}
}
}
return encKeys;
}
// Used in Krb5AcceptCredential, self
public EncryptionKey(byte[] keyValue,
int keyType,
Integer kvno) {
if (keyValue != null) {
this.keyValue = new byte[keyValue.length];
System.arraycopy(keyValue, 0, this.keyValue, 0, keyValue.length);
} else {
throw new IllegalArgumentException("EncryptionKey: " +
"Key bytes cannot be null!");
}
this.keyType = keyType;
this.kvno = kvno;
}
/**
* Constructs an EncryptionKey by using the specified key type and key
* value. It is used to recover the key when retrieving data from
* credential cache file.
*
*/
// Used in JSSE (KerberosWrapper), Credentials,
// javax.security.auth.kerberos.KeyImpl
public EncryptionKey(int keyType,
byte[] keyValue) {
this(keyValue, keyType, null);
}
private static byte[] stringToKey(char[] password, String salt,
byte[] s2kparams, int keyType) throws KrbCryptoException {
char[] slt = salt.toCharArray();
char[] pwsalt = new char[password.length + slt.length];
System.arraycopy(password, 0, pwsalt, 0, password.length);
System.arraycopy(slt, 0, pwsalt, password.length, slt.length);
Arrays.fill(slt, '0');
try {
switch (keyType) {
case EncryptedData.ETYPE_DES_CBC_CRC:
case EncryptedData.ETYPE_DES_CBC_MD5:
return Des.string_to_key_bytes(pwsalt);
case EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD:
return Des3.stringToKey(pwsalt);
case EncryptedData.ETYPE_ARCFOUR_HMAC:
return ArcFourHmac.stringToKey(password);
case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
return Aes128.stringToKey(password, salt, s2kparams);
case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
return Aes256.stringToKey(password, salt, s2kparams);
default:
throw new IllegalArgumentException("encryption type " +
EType.toString(keyType) + " not supported");
}
} catch (GeneralSecurityException e) {
KrbCryptoException ke = new KrbCryptoException(e.getMessage());
ke.initCause(e);
throw ke;
} finally {
Arrays.fill(pwsalt, '0');
}
}
// Used in javax.security.auth.kerberos.KeyImpl
public EncryptionKey(char[] password,
String salt,
String algorithm) throws KrbCryptoException {
if (algorithm == null || algorithm.equalsIgnoreCase("DES")
|| algorithm.equalsIgnoreCase("des-cbc-md5")) {
keyType = EncryptedData.ETYPE_DES_CBC_MD5;
} else if (algorithm.equalsIgnoreCase("des-cbc-crc")) {
keyType = EncryptedData.ETYPE_DES_CBC_CRC;
} else if (algorithm.equalsIgnoreCase("DESede")
|| algorithm.equalsIgnoreCase("des3-cbc-sha1-kd")) {
keyType = EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD;
} else if (algorithm.equalsIgnoreCase("AES128")
|| algorithm.equalsIgnoreCase("aes128-cts-hmac-sha1-96")) {
keyType = EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96;
} else if (algorithm.equalsIgnoreCase("ArcFourHmac")
|| algorithm.equalsIgnoreCase("rc4-hmac")) {
keyType = EncryptedData.ETYPE_ARCFOUR_HMAC;
} else if (algorithm.equalsIgnoreCase("AES256")
|| algorithm.equalsIgnoreCase("aes256-cts-hmac-sha1-96")) {
keyType = EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96;
// validate if AES256 is enabled
if (!EType.isSupported(keyType)) {
throw new IllegalArgumentException("Algorithm " + algorithm +
" not enabled");
}
} else {
throw new IllegalArgumentException("Algorithm " + algorithm +
" not supported");
}
keyValue = stringToKey(password, salt, null, keyType);
kvno = null;
}
/**
* Generates a sub-sessionkey from a given session key.
*
* Used in AcceptSecContextToken and KrbApReq by acceptor- and initiator-
* side respectively.
*/
public EncryptionKey(EncryptionKey key) throws KrbCryptoException {
// generate random sub-session key
keyValue = Confounder.bytes(key.keyValue.length);
for (int i = 0; i < keyValue.length; i++) {
keyValue[i] ^= key.keyValue[i];
}
keyType = key.keyType;
// check for key parity and weak keys
try {
// check for DES key
if ((keyType == EncryptedData.ETYPE_DES_CBC_MD5) ||
(keyType == EncryptedData.ETYPE_DES_CBC_CRC)) {
// fix DES key parity
if (!DESKeySpec.isParityAdjusted(keyValue, 0)) {
keyValue = Des.set_parity(keyValue);
}
// check for weak key
if (DESKeySpec.isWeak(keyValue, 0)) {
keyValue[7] = (byte)(keyValue[7] ^ 0xF0);
}
}
// check for 3DES key
if (keyType == EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
// fix 3DES key parity
if (!DESedeKeySpec.isParityAdjusted(keyValue, 0)) {
keyValue = Des3.parityFix(keyValue);
}
// check for weak keys
byte[] oneKey = new byte[8];
for (int i=0; i<keyValue.length; i+=8) {
System.arraycopy(keyValue, i, oneKey, 0, 8);
if (DESKeySpec.isWeak(oneKey, 0)) {
keyValue[i+7] = (byte)(keyValue[i+7] ^ 0xF0);
}
}
}
} catch (GeneralSecurityException e) {
KrbCryptoException ke = new KrbCryptoException(e.getMessage());
ke.initCause(e);
throw ke;
}
}
/**
* Constructs an instance of EncryptionKey type.
* @param encoding a single DER-encoded value.
* @exception Asn1Exception if an error occurs while decoding an ASN1
* encoded data.
* @exception IOException if an I/O error occurs while reading encoded
* data.
*
*
*/
// Used in javax.security.auth.kerberos.KeyImpl
public EncryptionKey(DerValue encoding) throws Asn1Exception, IOException {
DerValue der;
if (encoding.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte)0x1F) == (byte)0x00) {
keyType = der.getData().getBigInteger().intValue();
}
else
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
der = encoding.getData().getDerValue();
if ((der.getTag() & (byte)0x1F) == (byte)0x01) {
keyValue = der.getData().getOctetString();
}
else
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
if (der.getData().available() > 0) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
}
/**
* Returns the ASN.1 encoding of this EncryptionKey.
*
* <pre>{@code
* EncryptionKey ::= SEQUENCE {
* keytype[0] INTEGER,
* keyvalue[1] OCTET STRING }
* }</pre>
*
* <p>
* This definition reflects the Network Working Group RFC 4120
* specification available at
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
*
* @return byte array of encoded EncryptionKey object.
* @exception Asn1Exception if an error occurs while decoding an ASN1
* encoded data.
* @exception IOException if an I/O error occurs while reading encoded
* data.
*
*/
public synchronized byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
DerOutputStream temp = new DerOutputStream();
temp.putInteger(keyType);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
(byte)0x00), temp);
temp = new DerOutputStream();
temp.putOctetString(keyValue);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
(byte)0x01), temp);
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
return temp.toByteArray();
}
public synchronized void destroy() {
if (keyValue != null)
for (int i = 0; i < keyValue.length; i++)
keyValue[i] = 0;
}
/**
* Parse (unmarshal) an Encryption key from a DER input stream. This form
* parsing might be used when expanding a value which is part of
* a constructed sequence and uses explicitly tagged type.
*
* @param data the Der input stream value, which contains one or more
* marshaled value.
* @param explicitTag tag number.
* @param optional indicate if this data field is optional
* @exception Asn1Exception if an error occurs while decoding an ASN1
* encoded data.
* @exception IOException if an I/O error occurs while reading encoded
* data.
* @return an instance of EncryptionKey.
*
*/
public static EncryptionKey parse(DerInputStream data, byte
explicitTag, boolean optional) throws
Asn1Exception, IOException {
if ((optional) && (((byte)data.peekByte() & (byte)0x1F) !=
explicitTag)) {
return null;
}
DerValue der = data.getDerValue();
if (explicitTag != (der.getTag() & (byte)0x1F)) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
} else {
DerValue subDer = der.getData().getDerValue();
return new EncryptionKey(subDer);
}
}
/**
* Writes key value in FCC format to a <code>CCacheOutputStream</code>.
*
* @param cos a <code>CCacheOutputStream</code> to be written to.
* @exception IOException if an I/O exception occurs.
* @see sun.security.krb5.internal.ccache.CCacheOutputStream
*
*/
public synchronized void writeKey(CCacheOutputStream cos)
throws IOException {
cos.write16(keyType);
// we use KRB5_FCC_FVNO_3
cos.write16(keyType); // key type is recorded twice.
cos.write32(keyValue.length);
for (int i = 0; i < keyValue.length; i++) {
cos.write8(keyValue[i]);
}
}
public String toString() {
return new String("EncryptionKey: keyType=" + keyType
+ " kvno=" + kvno
+ " keyValue (hex dump)="
+ (keyValue == null || keyValue.length == 0 ?
" Empty Key" : '\n'
+ Krb5.hexDumper.encodeBuffer(keyValue)
+ '\n'));
}
/**
* Find a key with given etype
*/
public static EncryptionKey findKey(int etype, EncryptionKey[] keys)
throws KrbException {
return findKey(etype, null, keys);
}
/**
* Determines if a kvno matches another kvno. Used in the method
* findKey(type, kvno, keys). Always returns true if either input
* is null or zero, in case any side does not have kvno info available.
*
* Note: zero is included because N/A is not a legal value for kvno
* in javax.security.auth.kerberos.KerberosKey. Therefore, the info
* that the kvno is N/A might be lost when converting between this
* class and KerberosKey.
*/
private static boolean versionMatches(Integer v1, Integer v2) {
if (v1 == null || v1 == 0 || v2 == null || v2 == 0) {
return true;
}
return v1.equals(v2);
}
/**
* Find a key with given etype and kvno
* @param kvno if null, return any (first?) key
*/
public static EncryptionKey findKey(int etype, Integer kvno, EncryptionKey[] keys)
throws KrbException {
// check if encryption type is supported
if (!EType.isSupported(etype)) {
throw new KrbException("Encryption type " +
EType.toString(etype) + " is not supported/enabled");
}
int ktype;
boolean etypeFound = false;
// When no matched kvno is found, returns tke key of the same
// etype with the highest kvno
int kvno_found = 0;
EncryptionKey key_found = null;
for (int i = 0; i < keys.length; i++) {
ktype = keys[i].getEType();
if (EType.isSupported(ktype)) {
Integer kv = keys[i].getKeyVersionNumber();
if (etype == ktype) {
etypeFound = true;
if (versionMatches(kvno, kv)) {
return keys[i];
} else if (kv > kvno_found) {
// kv is not null
key_found = keys[i];
kvno_found = kv;
}
}
}
}
// Key not found.
// allow DES key to be used for the DES etypes
if ((etype == EncryptedData.ETYPE_DES_CBC_CRC ||
etype == EncryptedData.ETYPE_DES_CBC_MD5)) {
for (int i = 0; i < keys.length; i++) {
ktype = keys[i].getEType();
if (ktype == EncryptedData.ETYPE_DES_CBC_CRC ||
ktype == EncryptedData.ETYPE_DES_CBC_MD5) {
Integer kv = keys[i].getKeyVersionNumber();
etypeFound = true;
if (versionMatches(kvno, kv)) {
return new EncryptionKey(etype, keys[i].getBytes());
} else if (kv > kvno_found) {
key_found = new EncryptionKey(etype, keys[i].getBytes());
kvno_found = kv;
}
}
}
}
if (etypeFound) {
return key_found;
// For compatibility, will not fail here.
//throw new KrbException(Krb5.KRB_AP_ERR_BADKEYVER);
}
return null;
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.krb5;
import javax.security.auth.kerberos.KeyTab;
import sun.security.krb5.EncryptionKey;
import sun.security.krb5.PrincipalName;
/**
* An unsafe tunnel to get non-public access to classes in the
* javax.security.auth.kerberos package.
*/
public interface JavaxSecurityAuthKerberosAccess {
/**
* Returns a snapshot to the backing keytab
*/
public sun.security.krb5.internal.ktab.KeyTab keyTabTakeSnapshot(
KeyTab ktab);
}

View file

@ -0,0 +1,555 @@
/*
* Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import java.security.PrivilegedAction;
import java.security.Security;
import java.util.Locale;
import sun.security.krb5.internal.Krb5;
import sun.security.krb5.internal.NetClient;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.StringTokenizer;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
import sun.security.krb5.internal.KRBError;
/**
* KDC-REQ/KDC-REP communication. No more base class for KrbAsReq and
* KrbTgsReq. This class is now communication only.
*/
public final class KdcComm {
// The following settings can be configured in [libdefaults]
// section of krb5.conf, which are global for all realms. Each of
// them can also be defined in a realm, which overrides value here.
/**
* max retry time for a single KDC, default Krb5.KDC_RETRY_LIMIT (3)
*/
private static int defaultKdcRetryLimit;
/**
* timeout requesting a ticket from KDC, in millisec, default 30 sec
*/
private static int defaultKdcTimeout;
/**
* max UDP packet size, default unlimited (-1)
*/
private static int defaultUdpPrefLimit;
private static final boolean DEBUG = Krb5.DEBUG;
private static final String BAD_POLICY_KEY = "krb5.kdc.bad.policy";
/**
* What to do when a KDC is unavailable, specified in the
* java.security file with key krb5.kdc.bad.policy.
* Possible values can be TRY_LAST or TRY_LESS. Reloaded when refreshed.
*/
private enum BpType {
NONE, TRY_LAST, TRY_LESS
}
private static int tryLessMaxRetries = 1;
private static int tryLessTimeout = 5000;
private static BpType badPolicy;
static {
initStatic();
}
/**
* Read global settings
*/
public static void initStatic() {
String value = AccessController.doPrivileged(
new PrivilegedAction<String>() {
public String run() {
return Security.getProperty(BAD_POLICY_KEY);
}
});
if (value != null) {
value = value.toLowerCase(Locale.ENGLISH);
String[] ss = value.split(":");
if ("tryless".equals(ss[0])) {
if (ss.length > 1) {
String[] params = ss[1].split(",");
try {
int tmp0 = Integer.parseInt(params[0]);
if (params.length > 1) {
tryLessTimeout = Integer.parseInt(params[1]);
}
// Assign here in case of exception at params[1]
tryLessMaxRetries = tmp0;
} catch (NumberFormatException nfe) {
// Ignored. Please note that tryLess is recognized and
// used, parameters using default values
if (DEBUG) {
System.out.println("Invalid " + BAD_POLICY_KEY +
" parameter for tryLess: " +
value + ", use default");
}
}
}
badPolicy = BpType.TRY_LESS;
} else if ("trylast".equals(ss[0])) {
badPolicy = BpType.TRY_LAST;
} else {
badPolicy = BpType.NONE;
}
} else {
badPolicy = BpType.NONE;
}
int timeout = -1;
int max_retries = -1;
int udp_pref_limit = -1;
try {
Config cfg = Config.getInstance();
String temp = cfg.get("libdefaults", "kdc_timeout");
timeout = parseTimeString(temp);
temp = cfg.get("libdefaults", "max_retries");
max_retries = parsePositiveIntString(temp);
temp = cfg.get("libdefaults", "udp_preference_limit");
udp_pref_limit = parsePositiveIntString(temp);
} catch (Exception exc) {
// ignore any exceptions; use default values
if (DEBUG) {
System.out.println ("Exception in getting KDC communication " +
"settings, using default value " +
exc.getMessage());
}
}
defaultKdcTimeout = timeout > 0 ? timeout : 30*1000; // 30 seconds
defaultKdcRetryLimit =
max_retries > 0 ? max_retries : Krb5.KDC_RETRY_LIMIT;
if (udp_pref_limit < 0) {
defaultUdpPrefLimit = Krb5.KDC_DEFAULT_UDP_PREF_LIMIT;
} else if (udp_pref_limit > Krb5.KDC_HARD_UDP_LIMIT) {
defaultUdpPrefLimit = Krb5.KDC_HARD_UDP_LIMIT;
} else {
defaultUdpPrefLimit = udp_pref_limit;
}
KdcAccessibility.reset();
}
/**
* The instance fields
*/
private String realm;
public KdcComm(String realm) throws KrbException {
if (realm == null) {
realm = Config.getInstance().getDefaultRealm();
if (realm == null) {
throw new KrbException(Krb5.KRB_ERR_GENERIC,
"Cannot find default realm");
}
}
this.realm = realm;
}
public byte[] send(byte[] obuf)
throws IOException, KrbException {
int udpPrefLimit = getRealmSpecificValue(
realm, "udp_preference_limit", defaultUdpPrefLimit);
boolean useTCP = (udpPrefLimit > 0 &&
(obuf != null && obuf.length > udpPrefLimit));
return send(obuf, useTCP);
}
private byte[] send(byte[] obuf, boolean useTCP)
throws IOException, KrbException {
if (obuf == null)
return null;
Config cfg = Config.getInstance();
if (realm == null) {
realm = cfg.getDefaultRealm();
if (realm == null) {
throw new KrbException(Krb5.KRB_ERR_GENERIC,
"Cannot find default realm");
}
}
String kdcList = cfg.getKDCList(realm);
if (kdcList == null) {
throw new KrbException("Cannot get kdc for realm " + realm);
}
// tempKdc may include the port number also
Iterator<String> tempKdc = KdcAccessibility.list(kdcList).iterator();
if (!tempKdc.hasNext()) {
throw new KrbException("Cannot get kdc for realm " + realm);
}
byte[] ibuf = null;
try {
ibuf = sendIfPossible(obuf, tempKdc.next(), useTCP);
} catch(Exception first) {
boolean ok = false;
while(tempKdc.hasNext()) {
try {
ibuf = sendIfPossible(obuf, tempKdc.next(), useTCP);
ok = true;
break;
} catch(Exception ignore) {}
}
if (!ok) throw first;
}
if (ibuf == null) {
throw new IOException("Cannot get a KDC reply");
}
return ibuf;
}
// send the AS Request to the specified KDC
// failover to using TCP if useTCP is not set and response is too big
private byte[] sendIfPossible(byte[] obuf, String tempKdc, boolean useTCP)
throws IOException, KrbException {
try {
byte[] ibuf = send(obuf, tempKdc, useTCP);
KRBError ke = null;
try {
ke = new KRBError(ibuf);
} catch (Exception e) {
// OK
}
if (ke != null && ke.getErrorCode() ==
Krb5.KRB_ERR_RESPONSE_TOO_BIG) {
ibuf = send(obuf, tempKdc, true);
}
KdcAccessibility.removeBad(tempKdc);
return ibuf;
} catch(Exception e) {
if (DEBUG) {
System.out.println(">>> KrbKdcReq send: error trying " +
tempKdc);
e.printStackTrace(System.out);
}
KdcAccessibility.addBad(tempKdc);
throw e;
}
}
// send the AS Request to the specified KDC
private byte[] send(byte[] obuf, String tempKdc, boolean useTCP)
throws IOException, KrbException {
if (obuf == null)
return null;
int port = Krb5.KDC_INET_DEFAULT_PORT;
int retries = getRealmSpecificValue(
realm, "max_retries", defaultKdcRetryLimit);
int timeout = getRealmSpecificValue(
realm, "kdc_timeout", defaultKdcTimeout);
if (badPolicy == BpType.TRY_LESS &&
KdcAccessibility.isBad(tempKdc)) {
if (retries > tryLessMaxRetries) {
retries = tryLessMaxRetries; // less retries
}
if (timeout > tryLessTimeout) {
timeout = tryLessTimeout; // less time
}
}
String kdc = null;
String portStr = null;
if (tempKdc.charAt(0) == '[') { // Explicit IPv6 in []
int pos = tempKdc.indexOf(']', 1);
if (pos == -1) {
throw new IOException("Illegal KDC: " + tempKdc);
}
kdc = tempKdc.substring(1, pos);
if (pos != tempKdc.length() - 1) { // with port number
if (tempKdc.charAt(pos+1) != ':') {
throw new IOException("Illegal KDC: " + tempKdc);
}
portStr = tempKdc.substring(pos+2);
}
} else {
int colon = tempKdc.indexOf(':');
if (colon == -1) { // Hostname or IPv4 host only
kdc = tempKdc;
} else {
int nextColon = tempKdc.indexOf(':', colon+1);
if (nextColon > 0) { // >=2 ":", IPv6 with no port
kdc = tempKdc;
} else { // 1 ":", hostname or IPv4 with port
kdc = tempKdc.substring(0, colon);
portStr = tempKdc.substring(colon+1);
}
}
}
if (portStr != null) {
int tempPort = parsePositiveIntString(portStr);
if (tempPort > 0)
port = tempPort;
}
if (DEBUG) {
System.out.println(">>> KrbKdcReq send: kdc=" + kdc
+ (useTCP ? " TCP:":" UDP:")
+ port + ", timeout="
+ timeout
+ ", number of retries ="
+ retries
+ ", #bytes=" + obuf.length);
}
KdcCommunication kdcCommunication =
new KdcCommunication(kdc, port, useTCP, timeout, retries, obuf);
try {
byte[] ibuf = AccessController.doPrivileged(kdcCommunication);
if (DEBUG) {
System.out.println(">>> KrbKdcReq send: #bytes read="
+ (ibuf != null ? ibuf.length : 0));
}
return ibuf;
} catch (PrivilegedActionException e) {
Exception wrappedException = e.getException();
if (wrappedException instanceof IOException) {
throw (IOException) wrappedException;
} else {
throw (KrbException) wrappedException;
}
}
}
private static class KdcCommunication
implements PrivilegedExceptionAction<byte[]> {
private String kdc;
private int port;
private boolean useTCP;
private int timeout;
private int retries;
private byte[] obuf;
public KdcCommunication(String kdc, int port, boolean useTCP,
int timeout, int retries, byte[] obuf) {
this.kdc = kdc;
this.port = port;
this.useTCP = useTCP;
this.timeout = timeout;
this.retries = retries;
this.obuf = obuf;
}
// The caller only casts IOException and KrbException so don't
// add any new ones!
public byte[] run() throws IOException, KrbException {
byte[] ibuf = null;
for (int i=1; i <= retries; i++) {
String proto = useTCP?"TCP":"UDP";
if (DEBUG) {
System.out.println(">>> KDCCommunication: kdc=" + kdc
+ " " + proto + ":"
+ port + ", timeout="
+ timeout
+ ",Attempt =" + i
+ ", #bytes=" + obuf.length);
}
try (NetClient kdcClient = NetClient.getInstance(
proto, kdc, port, timeout)) {
kdcClient.send(obuf);
ibuf = kdcClient.receive();
break;
} catch (SocketTimeoutException se) {
if (DEBUG) {
System.out.println ("SocketTimeOutException with " +
"attempt: " + i);
}
if (i == retries) {
ibuf = null;
throw se;
}
}
}
return ibuf;
}
}
/**
* Parses a time value string. If it ends with "s", parses as seconds.
* Otherwise, parses as milliseconds.
* @param s the time string
* @return the integer value in milliseconds, or -1 if input is null or
* has an invalid format
*/
private static int parseTimeString(String s) {
if (s == null) {
return -1;
}
if (s.endsWith("s")) {
int seconds = parsePositiveIntString(s.substring(0, s.length()-1));
return (seconds < 0) ? -1 : (seconds*1000);
} else {
return parsePositiveIntString(s);
}
}
/**
* Returns krb5.conf setting of {@code key} for a specific realm,
* which can be:
* 1. defined in the sub-stanza for the given realm inside [realms], or
* 2. defined in [libdefaults], or
* 3. defValue
* @param realm the given realm in which the setting is requested. Returns
* the global setting if null
* @param key the key for the setting
* @param defValue default value
* @return a value for the key
*/
private int getRealmSpecificValue(String realm, String key, int defValue) {
int v = defValue;
if (realm == null) return v;
int temp = -1;
try {
String value =
Config.getInstance().get("realms", realm, key);
if (key.equals("kdc_timeout")) {
temp = parseTimeString(value);
} else {
temp = parsePositiveIntString(value);
}
} catch (Exception exc) {
// Ignored, defValue will be picked up
}
if (temp > 0) v = temp;
return v;
}
private static int parsePositiveIntString(String intString) {
if (intString == null)
return -1;
int ret = -1;
try {
ret = Integer.parseInt(intString);
} catch (Exception exc) {
return -1;
}
if (ret >= 0)
return ret;
return -1;
}
/**
* Maintains a KDC accessible list. Unavailable KDCs are put into a
* blacklist, when a KDC in the blacklist is available, it's removed
* from there. No insertion order in the blacklist.
*
* There are two methods to deal with KDCs in the blacklist. 1. Only try
* them when there's no KDC not on the blacklist. 2. Still try them, but
* with lesser number of retries and smaller timeout value.
*/
static class KdcAccessibility {
// Known bad KDCs
private static Set<String> bads = new HashSet<>();
private static synchronized void addBad(String kdc) {
if (DEBUG) {
System.out.println(">>> KdcAccessibility: add " + kdc);
}
bads.add(kdc);
}
private static synchronized void removeBad(String kdc) {
if (DEBUG) {
System.out.println(">>> KdcAccessibility: remove " + kdc);
}
bads.remove(kdc);
}
private static synchronized boolean isBad(String kdc) {
return bads.contains(kdc);
}
private static synchronized void reset() {
if (DEBUG) {
System.out.println(">>> KdcAccessibility: reset");
}
bads.clear();
}
// Returns a preferred KDC list by putting the bad ones at the end
private static synchronized List<String> list(String kdcList) {
StringTokenizer st = new StringTokenizer(kdcList);
List<String> list = new ArrayList<>();
if (badPolicy == BpType.TRY_LAST) {
List<String> badkdcs = new ArrayList<>();
while (st.hasMoreTokens()) {
String t = st.nextToken();
if (bads.contains(t)) badkdcs.add(t);
else list.add(t);
}
// Bad KDCs are put at last
list.addAll(badkdcs);
} else {
// All KDCs are returned in their original order,
// This include TRY_LESS and NONE
while (st.hasMoreTokens()) {
list.add(st.nextToken());
}
}
return list;
}
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.krb5;
import javax.security.auth.kerberos.KeyTab;
import jdk.internal.misc.Unsafe;
public class KerberosSecrets {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static JavaxSecurityAuthKerberosAccess javaxSecurityAuthKerberosAccess;
public static void setJavaxSecurityAuthKerberosAccess
(JavaxSecurityAuthKerberosAccess jsaka) {
javaxSecurityAuthKerberosAccess = jsaka;
}
public static JavaxSecurityAuthKerberosAccess
getJavaxSecurityAuthKerberosAccess() {
if (javaxSecurityAuthKerberosAccess == null)
unsafe.ensureClassInitialized(KeyTab.class);
return javaxSecurityAuthKerberosAccess;
}
}

View file

@ -0,0 +1,198 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.KeyUsage;
import sun.security.util.*;
import java.io.IOException;
/**
* This class encapsulates a KRB-AP-REP sent from the service to the
* client.
*/
public class KrbApRep {
private byte[] obuf;
private byte[] ibuf;
private EncAPRepPart encPart; // although in plain text
private APRep apRepMessg;
/**
* Constructs a KRB-AP-REP to send to a client.
* @throws KrbException
* @throws IOException
*/
// Used in AcceptSecContextToken
public KrbApRep(KrbApReq incomingReq,
boolean useSeqNumber,
EncryptionKey subKey)
throws KrbException, IOException {
SeqNumber seqNum = new LocalSeqNumber();
init(incomingReq, subKey, seqNum);
}
/**
* Constructs a KRB-AP-REQ from the bytes received from a service.
* @throws KrbException
* @throws IOException
*/
// Used in AcceptSecContextToken
public KrbApRep(byte[] message, Credentials tgtCreds,
KrbApReq outgoingReq) throws KrbException, IOException {
this(message, tgtCreds);
authenticate(outgoingReq);
}
private void init(KrbApReq apReq,
EncryptionKey subKey,
SeqNumber seqNumber)
throws KrbException, IOException {
createMessage(
apReq.getCreds().key,
apReq.getCtime(),
apReq.cusec(),
subKey,
seqNumber);
obuf = apRepMessg.asn1Encode();
}
/**
* Constructs a KrbApRep object.
* @param msg a byte array of reply message.
* @param tgs_creds client's credential.
* @exception KrbException
* @exception IOException
*/
private KrbApRep(byte[] msg, Credentials tgs_creds)
throws KrbException, IOException {
this(new DerValue(msg), tgs_creds);
}
/**
* Constructs a KrbApRep object.
* @param msg a byte array of reply message.
* @param tgs_creds client's credential.
* @exception KrbException
* @exception IOException
*/
private KrbApRep(DerValue encoding, Credentials tgs_creds)
throws KrbException, IOException {
APRep rep = null;
try {
rep = new APRep(encoding);
} catch (Asn1Exception e) {
rep = null;
KRBError err = new KRBError(encoding);
String errStr = err.getErrorString();
String eText;
if (errStr.charAt(errStr.length() - 1) == 0)
eText = errStr.substring(0, errStr.length() - 1);
else
eText = errStr;
KrbException ke = new KrbException(err.getErrorCode(), eText);
ke.initCause(e);
throw ke;
}
byte[] temp = rep.encPart.decrypt(tgs_creds.key,
KeyUsage.KU_ENC_AP_REP_PART);
byte[] enc_ap_rep_part = rep.encPart.reset(temp);
encoding = new DerValue(enc_ap_rep_part);
encPart = new EncAPRepPart(encoding);
}
private void authenticate(KrbApReq apReq)
throws KrbException, IOException {
if (encPart.ctime.getSeconds() != apReq.getCtime().getSeconds() ||
encPart.cusec != apReq.getCtime().getMicroSeconds())
throw new KrbApErrException(Krb5.KRB_AP_ERR_MUT_FAIL);
}
/**
* Returns the optional subkey stored in
* this message. Returns null if none is stored.
*/
public EncryptionKey getSubKey() {
// XXX Can encPart be null
return encPart.getSubKey();
}
/**
* Returns the optional sequence number stored in the
* this message. Returns null if none is stored.
*/
public Integer getSeqNumber() {
// XXX Can encPart be null
return encPart.getSeqNumber();
}
/**
* Returns the ASN.1 encoding that should be sent to the peer.
*/
public byte[] getMessage() {
return obuf;
}
private void createMessage(
EncryptionKey key,
KerberosTime ctime,
int cusec,
EncryptionKey subKey,
SeqNumber seqNumber)
throws Asn1Exception, IOException,
KdcErrException, KrbCryptoException {
Integer seqno = null;
if (seqNumber != null)
seqno = seqNumber.current();
encPart = new EncAPRepPart(ctime,
cusec,
subKey,
seqno);
byte[] encPartEncoding = encPart.asn1Encode();
EncryptedData encEncPart = new EncryptedData(key, encPartEncoding,
KeyUsage.KU_ENC_AP_REP_PART);
apRepMessg = new APRep(encEncPart);
}
}

View file

@ -0,0 +1,515 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
import sun.security.jgss.krb5.Krb5AcceptCredential;
import java.net.InetAddress;
import sun.security.util.*;
import java.io.IOException;
import java.util.Arrays;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import sun.security.krb5.internal.rcache.AuthTimeWithHash;
/**
* This class encapsulates a KRB-AP-REQ that a client sends to a
* server for authentication.
*/
public class KrbApReq {
private byte[] obuf;
private KerberosTime ctime;
private int cusec;
private Authenticator authenticator;
private Credentials creds;
private APReq apReqMessg;
// Used by acceptor side
private static ReplayCache rcache = ReplayCache.getInstance();
private static boolean DEBUG = Krb5.DEBUG;
private static final char[] hexConst = "0123456789ABCDEF".toCharArray();
/**
* Constructs an AP-REQ message to send to the peer.
* @param tgsCred the <code>Credentials</code> to be used to construct the
* AP Request protocol message.
* @param mutualRequired Whether mutual authentication is required
* @param useSubkey Whether the subkey is to be used to protect this
* specific application session. If this is not set then the
* session key from the ticket will be used.
* @throws KrbException for any Kerberos protocol specific error
* @throws IOException for any IO related errors
* (e.g. socket operations)
*/
/*
// Not Used
public KrbApReq(Credentials tgsCred,
boolean mutualRequired,
boolean useSubKey,
boolean useSeqNumber) throws Asn1Exception,
KrbCryptoException, KrbException, IOException {
this(tgsCred, mutualRequired, useSubKey, useSeqNumber, null);
}
*/
/**
* Constructs an AP-REQ message to send to the peer.
* @param tgsCred the <code>Credentials</code> to be used to construct the
* AP Request protocol message.
* @param mutualRequired Whether mutual authentication is required
* @param useSubKey Whether the subkey is to be used to protect this
* specific application session. If this is not set then the
* session key from the ticket will be used.
* @param cksum checksum of the application data that accompanies
* the KRB_AP_REQ.
* @throws KrbException for any Kerberos protocol specific error
* @throws IOException for any IO related errors
* (e.g. socket operations)
*/
// Used in InitSecContextToken
public KrbApReq(Credentials tgsCred,
boolean mutualRequired,
boolean useSubKey,
boolean useSeqNumber,
Checksum cksum) throws Asn1Exception,
KrbCryptoException, KrbException, IOException {
APOptions apOptions = (mutualRequired?
new APOptions(Krb5.AP_OPTS_MUTUAL_REQUIRED):
new APOptions());
if (DEBUG)
System.out.println(">>> KrbApReq: APOptions are " + apOptions);
EncryptionKey subKey = (useSubKey?
new EncryptionKey(tgsCred.getSessionKey()):
null);
SeqNumber seqNum = new LocalSeqNumber();
init(apOptions,
tgsCred,
cksum,
subKey,
seqNum,
null, // AuthorizationData authzData
KeyUsage.KU_AP_REQ_AUTHENTICATOR);
}
/**
* Constructs an AP-REQ message from the bytes received from the
* peer.
* @param message The message received from the peer
* @param cred <code>KrbAcceptCredential</code> containing keys to decrypt
* the message; key selected will depend on etype used to encrypt data
* @throws KrbException for any Kerberos protocol specific error
* @throws IOException for any IO related errors
* (e.g. socket operations)
*/
// Used in InitSecContextToken (for AP_REQ and not TGS REQ)
public KrbApReq(byte[] message,
Krb5AcceptCredential cred,
InetAddress initiator)
throws KrbException, IOException {
obuf = message;
if (apReqMessg == null)
decode();
authenticate(cred, initiator);
}
/**
* Constructs an AP-REQ message from the bytes received from the
* peer.
* @param value The <code>DerValue</code> that contains the
* DER enoded AP-REQ protocol message
* @param keys <code>EncrtyptionKey</code>s to decrypt the message;
*
* @throws KrbException for any Kerberos protocol specific error
* @throws IOException for any IO related errors
* (e.g. socket operations)
*/
/*
public KrbApReq(DerValue value, EncryptionKey[] key, InetAddress initiator)
throws KrbException, IOException {
obuf = value.toByteArray();
if (apReqMessg == null)
decode(value);
authenticate(keys, initiator);
}
KrbApReq(APOptions options,
Credentials tgs_creds,
Checksum cksum,
EncryptionKey subKey,
SeqNumber seqNumber,
AuthorizationData authorizationData)
throws KrbException, IOException {
init(options, tgs_creds, cksum, subKey, seqNumber, authorizationData);
}
*/
/** used by KrbTgsReq **/
KrbApReq(APOptions apOptions,
Ticket ticket,
EncryptionKey key,
PrincipalName cname,
Checksum cksum,
KerberosTime ctime,
EncryptionKey subKey,
SeqNumber seqNumber,
AuthorizationData authorizationData)
throws Asn1Exception, IOException,
KdcErrException, KrbCryptoException {
init(apOptions, ticket, key, cname,
cksum, ctime, subKey, seqNumber, authorizationData,
KeyUsage.KU_PA_TGS_REQ_AUTHENTICATOR);
}
private void init(APOptions options,
Credentials tgs_creds,
Checksum cksum,
EncryptionKey subKey,
SeqNumber seqNumber,
AuthorizationData authorizationData,
int usage)
throws KrbException, IOException {
ctime = KerberosTime.now();
init(options,
tgs_creds.ticket,
tgs_creds.key,
tgs_creds.client,
cksum,
ctime,
subKey,
seqNumber,
authorizationData,
usage);
}
private void init(APOptions apOptions,
Ticket ticket,
EncryptionKey key,
PrincipalName cname,
Checksum cksum,
KerberosTime ctime,
EncryptionKey subKey,
SeqNumber seqNumber,
AuthorizationData authorizationData,
int usage)
throws Asn1Exception, IOException,
KdcErrException, KrbCryptoException {
createMessage(apOptions, ticket, key, cname,
cksum, ctime, subKey, seqNumber, authorizationData,
usage);
obuf = apReqMessg.asn1Encode();
}
void decode() throws KrbException, IOException {
DerValue encoding = new DerValue(obuf);
decode(encoding);
}
void decode(DerValue encoding) throws KrbException, IOException {
apReqMessg = null;
try {
apReqMessg = new APReq(encoding);
} catch (Asn1Exception e) {
apReqMessg = null;
KRBError err = new KRBError(encoding);
String errStr = err.getErrorString();
String eText;
if (errStr.charAt(errStr.length() - 1) == 0)
eText = errStr.substring(0, errStr.length() - 1);
else
eText = errStr;
KrbException ke = new KrbException(err.getErrorCode(), eText);
ke.initCause(e);
throw ke;
}
}
private void authenticate(Krb5AcceptCredential cred, InetAddress initiator)
throws KrbException, IOException {
int encPartKeyType = apReqMessg.ticket.encPart.getEType();
Integer kvno = apReqMessg.ticket.encPart.getKeyVersionNumber();
EncryptionKey[] keys = cred.getKrb5EncryptionKeys(apReqMessg.ticket.sname);
EncryptionKey dkey = EncryptionKey.findKey(encPartKeyType, kvno, keys);
if (dkey == null) {
throw new KrbException(Krb5.API_INVALID_ARG,
"Cannot find key of appropriate type to decrypt AP-REQ - " +
EType.toString(encPartKeyType));
}
byte[] bytes = apReqMessg.ticket.encPart.decrypt(dkey,
KeyUsage.KU_TICKET);
byte[] temp = apReqMessg.ticket.encPart.reset(bytes);
EncTicketPart enc_ticketPart = new EncTicketPart(temp);
checkPermittedEType(enc_ticketPart.key.getEType());
byte[] bytes2 = apReqMessg.authenticator.decrypt(enc_ticketPart.key,
KeyUsage.KU_AP_REQ_AUTHENTICATOR);
byte[] temp2 = apReqMessg.authenticator.reset(bytes2);
authenticator = new Authenticator(temp2);
ctime = authenticator.ctime;
cusec = authenticator.cusec;
authenticator.ctime =
authenticator.ctime.withMicroSeconds(authenticator.cusec);
if (!authenticator.cname.equals(enc_ticketPart.cname)) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_BADMATCH);
}
if (!authenticator.ctime.inClockSkew())
throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW);
String alg = AuthTimeWithHash.DEFAULT_HASH_ALG;
byte[] hash;
try {
hash = MessageDigest.getInstance(AuthTimeWithHash.realAlg(alg))
.digest(apReqMessg.authenticator.cipher);
} catch (NoSuchAlgorithmException ex) {
throw new AssertionError("Impossible " + alg);
}
char[] h = new char[hash.length * 2];
for (int i=0; i<hash.length; i++) {
h[2*i] = hexConst[(hash[i]&0xff)>>4];
h[2*i+1] = hexConst[hash[i]&0xf];
}
AuthTimeWithHash time = new AuthTimeWithHash(
authenticator.cname.toString(),
apReqMessg.ticket.sname.toString(),
authenticator.ctime.getSeconds(),
authenticator.cusec,
alg,
new String(h));
rcache.checkAndStore(KerberosTime.now(), time);
if (initiator != null) {
// sender host address
HostAddress sender = new HostAddress(initiator);
if (enc_ticketPart.caddr != null
&& !enc_ticketPart.caddr.inList(sender)) {
if (DEBUG) {
System.out.println(">>> KrbApReq: initiator is "
+ sender.getInetAddress()
+ ", but caddr is "
+ Arrays.toString(
enc_ticketPart.caddr.getInetAddresses()));
}
throw new KrbApErrException(Krb5.KRB_AP_ERR_BADADDR);
}
}
// XXX check for repeated authenticator
// if found
// throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT);
// else
// save authenticator to check for later
KerberosTime now = KerberosTime.now();
if ((enc_ticketPart.starttime != null &&
enc_ticketPart.starttime.greaterThanWRTClockSkew(now)) ||
enc_ticketPart.flags.get(Krb5.TKT_OPTS_INVALID))
throw new KrbApErrException(Krb5.KRB_AP_ERR_TKT_NYV);
// if the current time is later than end time by more
// than the allowable clock skew, throws ticket expired exception.
if (enc_ticketPart.endtime != null &&
now.greaterThanWRTClockSkew(enc_ticketPart.endtime)) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_TKT_EXPIRED);
}
creds = new Credentials(
apReqMessg.ticket,
authenticator.cname,
apReqMessg.ticket.sname,
enc_ticketPart.key,
enc_ticketPart.flags,
enc_ticketPart.authtime,
enc_ticketPart.starttime,
enc_ticketPart.endtime,
enc_ticketPart.renewTill,
enc_ticketPart.caddr,
enc_ticketPart.authorizationData);
if (DEBUG) {
System.out.println(">>> KrbApReq: authenticate succeed.");
}
}
/**
* Returns the credentials that are contained in the ticket that
* is part of this AP-REQ.
*/
public Credentials getCreds() {
return creds;
}
KerberosTime getCtime() {
if (ctime != null)
return ctime;
return authenticator.ctime;
}
int cusec() {
return cusec;
}
APOptions getAPOptions() throws KrbException, IOException {
if (apReqMessg == null)
decode();
if (apReqMessg != null)
return apReqMessg.apOptions;
return null;
}
/**
* Returns true if mutual authentication is required and hence an
* AP-REP will need to be generated.
* @throws KrbException
* @throws IOException
*/
public boolean getMutualAuthRequired() throws KrbException, IOException {
if (apReqMessg == null)
decode();
if (apReqMessg != null)
return apReqMessg.apOptions.get(Krb5.AP_OPTS_MUTUAL_REQUIRED);
return false;
}
boolean useSessionKey() throws KrbException, IOException {
if (apReqMessg == null)
decode();
if (apReqMessg != null)
return apReqMessg.apOptions.get(Krb5.AP_OPTS_USE_SESSION_KEY);
return false;
}
/**
* Returns the optional subkey stored in the Authenticator for
* this message. Returns null if none is stored.
*/
public EncryptionKey getSubKey() {
// XXX Can authenticator be null
return authenticator.getSubKey();
}
/**
* Returns the optional sequence number stored in the
* Authenticator for this message. Returns null if none is
* stored.
*/
public Integer getSeqNumber() {
// XXX Can authenticator be null
return authenticator.getSeqNumber();
}
/**
* Returns the optional Checksum stored in the
* Authenticator for this message. Returns null if none is
* stored.
*/
public Checksum getChecksum() {
return authenticator.getChecksum();
}
/**
* Returns the ASN.1 encoding that should be sent to the peer.
*/
public byte[] getMessage() {
return obuf;
}
/**
* Returns the principal name of the client that generated this
* message.
*/
public PrincipalName getClient() {
return creds.getClient();
}
private void createMessage(APOptions apOptions,
Ticket ticket,
EncryptionKey key,
PrincipalName cname,
Checksum cksum,
KerberosTime ctime,
EncryptionKey subKey,
SeqNumber seqNumber,
AuthorizationData authorizationData,
int usage)
throws Asn1Exception, IOException,
KdcErrException, KrbCryptoException {
Integer seqno = null;
if (seqNumber != null)
seqno = seqNumber.current();
authenticator =
new Authenticator(cname,
cksum,
ctime.getMicroSeconds(),
ctime,
subKey,
seqno,
authorizationData);
byte[] temp = authenticator.asn1Encode();
EncryptedData encAuthenticator =
new EncryptedData(key, temp, usage);
apReqMessg =
new APReq(apOptions, ticket, encAuthenticator);
}
// Check that key is one of the permitted types
private static void checkPermittedEType(int target) throws KrbException {
int[] etypes = EType.getDefaults("permitted_enctypes");
if (!EType.isSupported(target, etypes)) {
throw new KrbException(EType.toString(target) +
" encryption type not in permitted_enctypes list");
}
}
}

View file

@ -0,0 +1,115 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
abstract class KrbAppMessage {
private static boolean DEBUG = Krb5.DEBUG;
/**
* Common checks for KRB-PRIV and KRB-SAFE
*/
void check(KerberosTime packetTimestamp,
Integer packetUsec,
Integer packetSeqNumber,
HostAddress packetSAddress,
HostAddress packetRAddress,
SeqNumber seqNumber,
HostAddress sAddress,
HostAddress rAddress,
boolean timestampRequired,
boolean seqNumberRequired,
PrincipalName packetPrincipal)
throws KrbApErrException {
if (!Krb5.AP_EMPTY_ADDRESSES_ALLOWED || sAddress != null) {
if (packetSAddress == null || sAddress == null ||
!packetSAddress.equals(sAddress)) {
if (DEBUG && packetSAddress == null) {
System.out.println("packetSAddress is null");
}
if (DEBUG && sAddress == null) {
System.out.println("sAddress is null");
}
throw new KrbApErrException(Krb5.KRB_AP_ERR_BADADDR);
}
}
if (!Krb5.AP_EMPTY_ADDRESSES_ALLOWED || rAddress != null) {
if (packetRAddress == null || rAddress == null ||
!packetRAddress.equals(rAddress))
throw new KrbApErrException(Krb5.KRB_AP_ERR_BADADDR);
}
if (packetTimestamp != null) {
if (packetUsec != null) {
packetTimestamp =
packetTimestamp.withMicroSeconds(packetUsec.intValue());
}
if (!packetTimestamp.inClockSkew()) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW);
}
} else {
if (timestampRequired) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW);
}
}
// XXX check replay cache
// if (rcache.repeated(packetTimestamp, packetUsec, packetSAddress))
// throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT);
// XXX consider moving up to api level
if (seqNumber == null && seqNumberRequired == true)
throw new KrbApErrException(Krb5.API_INVALID_ARG);
if (packetSeqNumber != null && seqNumber != null) {
if (packetSeqNumber.intValue() != seqNumber.current())
throw new KrbApErrException(Krb5.KRB_AP_ERR_BADORDER);
// should be done only when no more exceptions are possible
seqNumber.step();
} else {
if (seqNumberRequired) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_BADORDER);
}
}
// Must not be relaxed, per RFC 4120
if (packetTimestamp == null && packetSeqNumber == null)
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
// XXX check replay cache
// rcache.save_identifier(packetTimestamp, packetUsec, packetSAddress,
// packetPrincipal, pcaketRealm);
}
}

View file

@ -0,0 +1,184 @@
/*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.KeyUsage;
import sun.security.krb5.internal.crypto.EType;
import sun.security.util.*;
import java.io.IOException;
import java.util.Objects;
import javax.security.auth.kerberos.KeyTab;
import sun.security.jgss.krb5.Krb5Util;
/**
* This class encapsulates a AS-REP message that the KDC sends to the
* client.
*/
class KrbAsRep extends KrbKdcRep {
private ASRep rep; // The AS-REP message
private Credentials creds; // The Credentials provide by the AS-REP
// message, created by initiator after calling
// the decrypt() method
private boolean DEBUG = Krb5.DEBUG;
KrbAsRep(byte[] ibuf) throws
KrbException, Asn1Exception, IOException {
DerValue encoding = new DerValue(ibuf);
try {
rep = new ASRep(encoding);
} catch (Asn1Exception e) {
rep = null;
KRBError err = new KRBError(encoding);
String errStr = err.getErrorString();
String eText = null; // pick up text sent by the server (if any)
if (errStr != null && errStr.length() > 0) {
if (errStr.charAt(errStr.length() - 1) == 0)
eText = errStr.substring(0, errStr.length() - 1);
else
eText = errStr;
}
KrbException ke;
if (eText == null) {
// no text sent from server
ke = new KrbException(err);
} else {
if (DEBUG) {
System.out.println("KRBError received: " + eText);
}
// override default text with server text
ke = new KrbException(err, eText);
}
ke.initCause(e);
throw ke;
}
}
// KrbAsReqBuilder need to read back the PA for key generation
PAData[] getPA() {
return rep.pAData;
}
/**
* Called by KrbAsReqBuilder to resolve a AS-REP message using a keytab.
* @param ktab the keytab, not null
* @param asReq the original AS-REQ sent, used to validate AS-REP
* @param cname the user principal name, used to locate keys in ktab
*/
void decryptUsingKeyTab(KeyTab ktab, KrbAsReq asReq, PrincipalName cname)
throws KrbException, Asn1Exception, IOException {
EncryptionKey dkey = null;
int encPartKeyType = rep.encPart.getEType();
Integer encPartKvno = rep.encPart.kvno;
try {
dkey = EncryptionKey.findKey(encPartKeyType, encPartKvno,
Krb5Util.keysFromJavaxKeyTab(ktab, cname));
} catch (KrbException ke) {
if (ke.returnCode() == Krb5.KRB_AP_ERR_BADKEYVER) {
// Fallback to no kvno. In some cases, keytab is generated
// not by sysadmin but Java's ktab command
dkey = EncryptionKey.findKey(encPartKeyType,
Krb5Util.keysFromJavaxKeyTab(ktab, cname));
}
}
if (dkey == null) {
throw new KrbException(Krb5.API_INVALID_ARG,
"Cannot find key for type/kvno to decrypt AS REP - " +
EType.toString(encPartKeyType) + "/" + encPartKvno);
}
decrypt(dkey, asReq);
}
/**
* Called by KrbAsReqBuilder to resolve a AS-REP message using a password.
* @param password user provided password. not null
* @param asReq the original AS-REQ sent, used to validate AS-REP
* @param cname the user principal name, used to provide salt
*/
void decryptUsingPassword(char[] password,
KrbAsReq asReq, PrincipalName cname)
throws KrbException, Asn1Exception, IOException {
int encPartKeyType = rep.encPart.getEType();
EncryptionKey dkey = EncryptionKey.acquireSecretKey(
cname,
password,
encPartKeyType,
PAData.getSaltAndParams(encPartKeyType, rep.pAData));
decrypt(dkey, asReq);
}
/**
* Decrypts encrypted content inside AS-REP. Called by initiator.
* @param dkey the decryption key to use
* @param asReq the original AS-REQ sent, used to validate AS-REP
*/
private void decrypt(EncryptionKey dkey, KrbAsReq asReq)
throws KrbException, Asn1Exception, IOException {
byte[] enc_as_rep_bytes = rep.encPart.decrypt(dkey,
KeyUsage.KU_ENC_AS_REP_PART);
byte[] enc_as_rep_part = rep.encPart.reset(enc_as_rep_bytes);
DerValue encoding = new DerValue(enc_as_rep_part);
EncASRepPart enc_part = new EncASRepPart(encoding);
rep.encKDCRepPart = enc_part;
ASReq req = asReq.getMessage();
check(true, req, rep);
creds = new Credentials(
rep.ticket,
req.reqBody.cname,
enc_part.sname,
enc_part.key,
enc_part.flags,
enc_part.authtime,
enc_part.starttime,
enc_part.endtime,
enc_part.renewTill,
enc_part.caddr);
if (DEBUG) {
System.out.println(">>> KrbAsRep cons in KrbAsReq.getReply " +
req.reqBody.cname.getNameString());
}
}
Credentials getCreds() {
return Objects.requireNonNull(creds, "Creds not available yet.");
}
sun.security.krb5.internal.ccache.Credentials getCCreds() {
return new sun.security.krb5.internal.ccache.Credentials(rep);
}
}

View file

@ -0,0 +1,167 @@
/*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.Nonce;
import sun.security.krb5.internal.crypto.KeyUsage;
import java.io.IOException;
import java.time.Instant;
/**
* This class encapsulates the KRB-AS-REQ message that the client
* sends to the KDC.
*/
public class KrbAsReq {
private ASReq asReqMessg;
private boolean DEBUG = Krb5.DEBUG;
/**
* Constructs an AS-REQ message.
*/
// Can be null? has default?
public KrbAsReq(EncryptionKey pakey, // ok
KDCOptions options, // ok, new KDCOptions()
PrincipalName cname, // NO and must have realm
PrincipalName sname, // ok, krgtgt@CREALM
KerberosTime from, // ok
KerberosTime till, // ok, will use
KerberosTime rtime, // ok
int[] eTypes, // NO
HostAddresses addresses // ok
)
throws KrbException, IOException {
if (options == null) {
options = new KDCOptions();
}
// check if they are valid arguments. The optional fields should be
// consistent with settings in KDCOptions. Mar 17 2000
if (options.get(KDCOptions.FORWARDED) ||
options.get(KDCOptions.PROXY) ||
options.get(KDCOptions.ENC_TKT_IN_SKEY) ||
options.get(KDCOptions.RENEW) ||
options.get(KDCOptions.VALIDATE)) {
// this option is only specified in a request to the
// ticket-granting server
throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
}
if (options.get(KDCOptions.POSTDATED)) {
// if (from == null)
// throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
} else {
if (from != null) from = null;
}
PAData[] paData = null;
if (pakey != null) {
PAEncTSEnc ts = new PAEncTSEnc();
byte[] temp = ts.asn1Encode();
EncryptedData encTs = new EncryptedData(pakey, temp,
KeyUsage.KU_PA_ENC_TS);
paData = new PAData[1];
paData[0] = new PAData( Krb5.PA_ENC_TIMESTAMP,
encTs.asn1Encode());
}
if (cname.getRealm() == null) {
throw new RealmException(Krb5.REALM_NULL,
"default realm not specified ");
}
if (DEBUG) {
System.out.println(">>> KrbAsReq creating message");
}
Config cfg = Config.getInstance();
// check to use addresses in tickets
if (addresses == null && cfg.useAddresses()) {
addresses = HostAddresses.getLocalAddresses();
}
if (sname == null) {
String realm = cname.getRealmAsString();
sname = PrincipalName.tgsService(realm, realm);
}
if (till == null) {
String d = cfg.get("libdefaults", "ticket_lifetime");
if (d != null) {
till = new KerberosTime(Instant.now().plusSeconds(Config.duration(d)));
} else {
till = new KerberosTime(0); // Choose KDC maximum allowed
}
}
if (rtime == null) {
String d = cfg.get("libdefaults", "renew_lifetime");
if (d != null) {
rtime = new KerberosTime(Instant.now().plusSeconds(Config.duration(d)));
}
}
if (rtime != null) {
options.set(KDCOptions.RENEWABLE, true);
if (till.greaterThan(rtime)) {
rtime = till;
}
}
// enc-authorization-data and additional-tickets never in AS-REQ
KDCReqBody kdc_req_body = new KDCReqBody(options,
cname,
sname,
from,
till,
rtime,
Nonce.value(),
eTypes,
addresses,
null,
null);
asReqMessg = new ASReq(
paData,
kdc_req_body);
}
byte[] encoding() throws IOException, Asn1Exception {
return asReqMessg.asn1Encode();
}
// Used by KrbAsRep to validate AS-REP
ASReq getMessage() {
return asReqMessg;
}
}

View file

@ -0,0 +1,411 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.krb5;
import java.io.IOException;
import java.util.Arrays;
import javax.security.auth.kerberos.KeyTab;
import sun.security.jgss.krb5.Krb5Util;
import sun.security.krb5.internal.HostAddresses;
import sun.security.krb5.internal.KDCOptions;
import sun.security.krb5.internal.KRBError;
import sun.security.krb5.internal.KerberosTime;
import sun.security.krb5.internal.Krb5;
import sun.security.krb5.internal.PAData;
import sun.security.krb5.internal.crypto.EType;
/**
* A manager class for AS-REQ communications.
*
* This class does:
* 1. Gather information to create AS-REQ
* 2. Create and send AS-REQ
* 3. Receive AS-REP and KRB-ERROR (-KRB_ERR_RESPONSE_TOO_BIG) and parse them
* 4. Emit credentials and secret keys (for JAAS storeKey=true with password)
*
* This class does not:
* 1. Deal with real communications (KdcComm does it, and TGS-REQ)
* a. Name of KDCs for a realm
* b. Server availability, timeout, UDP or TCP
* d. KRB_ERR_RESPONSE_TOO_BIG
* 2. Stores its own copy of password, this means:
* a. Do not change/wipe it before Builder finish
* b. Builder will not wipe it for you
*
* With this class:
* 1. KrbAsReq has only one constructor
* 2. Krb5LoginModule and Kinit call a single builder
* 3. Better handling of sensitive info
*
* @since 1.7
*/
public final class KrbAsReqBuilder {
// Common data for AS-REQ fields
private KDCOptions options;
private PrincipalName cname;
private PrincipalName sname;
private KerberosTime from;
private KerberosTime till;
private KerberosTime rtime;
private HostAddresses addresses;
// Secret source: can't be changed once assigned, only one (of the two
// sources) can be set to non-null
private final char[] password;
private final KeyTab ktab;
// Used to create a ENC-TIMESTAMP in the 2nd AS-REQ
private PAData[] paList; // PA-DATA from both KRB-ERROR and AS-REP.
// Used by getKeys() only.
// Only AS-REP should be enough per RFC,
// combined in case etypes are different.
// The generated and received:
private KrbAsReq req;
private KrbAsRep rep;
private static enum State {
INIT, // Initialized, can still add more initialization info
REQ_OK, // AS-REQ performed
DESTROYED, // Destroyed, not usable anymore
}
private State state;
// Called by other constructors
private void init(PrincipalName cname)
throws KrbException {
this.cname = cname;
state = State.INIT;
}
/**
* Creates a builder to be used by {@code cname} with existing keys.
*
* @param cname the client of the AS-REQ. Must not be null. Might have no
* realm, where default realm will be used. This realm will be the target
* realm for AS-REQ. I believe a client should only get initial TGT from
* its own realm.
* @param ktab must not be null. If empty, might be quite useless.
* This argument will neither be modified nor stored by the method.
* @throws KrbException
*/
public KrbAsReqBuilder(PrincipalName cname, KeyTab ktab)
throws KrbException {
init(cname);
this.ktab = ktab;
this.password = null;
}
/**
* Creates a builder to be used by {@code cname} with a known password.
*
* @param cname the client of the AS-REQ. Must not be null. Might have no
* realm, where default realm will be used. This realm will be the target
* realm for AS-REQ. I believe a client should only get initial TGT from
* its own realm.
* @param pass must not be null. This argument will neither be modified
* nor stored by the method.
* @throws KrbException
*/
public KrbAsReqBuilder(PrincipalName cname, char[] pass)
throws KrbException {
init(cname);
this.password = pass.clone();
this.ktab = null;
}
/**
* Retrieves an array of secret keys for the client. This is used when
* the client supplies password but need keys to act as an acceptor. For
* an initiator, it must be called after AS-REQ is performed (state is OK).
* For an acceptor, it can be called when this KrbAsReqBuilder object is
* constructed (state is INIT).
* @param isInitiator if the caller is an initiator
* @return generated keys from password. PA-DATA from server might be used.
* All "default_tkt_enctypes" keys will be generated, Never null.
* @throws IllegalStateException if not constructed from a password
* @throws KrbException
*/
public EncryptionKey[] getKeys(boolean isInitiator) throws KrbException {
checkState(isInitiator?State.REQ_OK:State.INIT, "Cannot get keys");
if (password != null) {
int[] eTypes = EType.getDefaults("default_tkt_enctypes");
EncryptionKey[] result = new EncryptionKey[eTypes.length];
/*
* Returns an array of keys. Before KrbAsReqBuilder, all etypes
* use the same salt which is either the default one or a new salt
* coming from PA-DATA. After KrbAsReqBuilder, each etype uses its
* own new salt from PA-DATA. For an etype with no PA-DATA new salt
* at all, what salt should it use?
*
* Commonly, the stored keys are only to be used by an acceptor to
* decrypt service ticket in AP-REQ. Most impls only allow keys
* from a keytab on acceptor, but unfortunately (?) Java supports
* acceptor using password. In this case, if the service ticket is
* encrypted using an etype which we don't have PA-DATA new salt,
* using the default salt might be wrong (say, case-insensitive
* user name). Instead, we would use the new salt of another etype.
*/
String salt = null; // the saved new salt
try {
for (int i=0; i<eTypes.length; i++) {
// First round, only calculate those have a PA entry
PAData.SaltAndParams snp =
PAData.getSaltAndParams(eTypes[i], paList);
if (snp != null) {
// Never uses a salt for rc4-hmac, it does not use
// a salt at all
if (eTypes[i] != EncryptedData.ETYPE_ARCFOUR_HMAC &&
snp.salt != null) {
salt = snp.salt;
}
result[i] = EncryptionKey.acquireSecretKey(cname,
password,
eTypes[i],
snp);
}
}
// No new salt from PA, maybe empty, maybe only rc4-hmac
if (salt == null) salt = cname.getSalt();
for (int i=0; i<eTypes.length; i++) {
// Second round, calculate those with no PA entry
if (result[i] == null) {
result[i] = EncryptionKey.acquireSecretKey(password,
salt,
eTypes[i],
null);
}
}
} catch (IOException ioe) {
KrbException ke = new KrbException(Krb5.ASN1_PARSE_ERROR);
ke.initCause(ioe);
throw ke;
}
return result;
} else {
throw new IllegalStateException("Required password not provided");
}
}
/**
* Sets or clears options. If cleared, default options will be used
* at creation time.
* @param options
*/
public void setOptions(KDCOptions options) {
checkState(State.INIT, "Cannot specify options");
this.options = options;
}
public void setTill(KerberosTime till) {
checkState(State.INIT, "Cannot specify till");
this.till = till;
}
public void setRTime(KerberosTime rtime) {
checkState(State.INIT, "Cannot specify rtime");
this.rtime = rtime;
}
/**
* Sets or clears target. If cleared, KrbAsReq might choose krbtgt
* for cname realm
* @param sname
*/
public void setTarget(PrincipalName sname) {
checkState(State.INIT, "Cannot specify target");
this.sname = sname;
}
/**
* Adds or clears addresses. KrbAsReq might add some if empty
* field not allowed
* @param addresses
*/
public void setAddresses(HostAddresses addresses) {
checkState(State.INIT, "Cannot specify addresses");
this.addresses = addresses;
}
/**
* Build a KrbAsReq object from all info fed above. Normally this method
* will be called twice: initial AS-REQ and second with pakey
* @param key null (initial AS-REQ) or pakey (with preauth)
* @return the KrbAsReq object
* @throws KrbException
* @throws IOException
*/
private KrbAsReq build(EncryptionKey key) throws KrbException, IOException {
int[] eTypes;
if (password != null) {
eTypes = EType.getDefaults("default_tkt_enctypes");
} else {
EncryptionKey[] ks = Krb5Util.keysFromJavaxKeyTab(ktab, cname);
eTypes = EType.getDefaults("default_tkt_enctypes",
ks);
for (EncryptionKey k: ks) k.destroy();
}
return new KrbAsReq(key,
options,
cname,
sname,
from,
till,
rtime,
eTypes,
addresses);
}
/**
* Parses AS-REP, decrypts enc-part, retrieves ticket and session key
* @throws KrbException
* @throws Asn1Exception
* @throws IOException
*/
private KrbAsReqBuilder resolve()
throws KrbException, Asn1Exception, IOException {
if (ktab != null) {
rep.decryptUsingKeyTab(ktab, req, cname);
} else {
rep.decryptUsingPassword(password, req, cname);
}
if (rep.getPA() != null) {
if (paList == null || paList.length == 0) {
paList = rep.getPA();
} else {
int extraLen = rep.getPA().length;
if (extraLen > 0) {
int oldLen = paList.length;
paList = Arrays.copyOf(paList, paList.length + extraLen);
System.arraycopy(rep.getPA(), 0, paList, oldLen, extraLen);
}
}
}
return this;
}
/**
* Communication until AS-REP or non preauth-related KRB-ERROR received
* @throws KrbException
* @throws IOException
*/
private KrbAsReqBuilder send() throws KrbException, IOException {
boolean preAuthFailedOnce = false;
KdcComm comm = new KdcComm(cname.getRealmAsString());
EncryptionKey pakey = null;
while (true) {
try {
req = build(pakey);
rep = new KrbAsRep(comm.send(req.encoding()));
return this;
} catch (KrbException ke) {
if (!preAuthFailedOnce && (
ke.returnCode() == Krb5.KDC_ERR_PREAUTH_FAILED ||
ke.returnCode() == Krb5.KDC_ERR_PREAUTH_REQUIRED)) {
if (Krb5.DEBUG) {
System.out.println("KrbAsReqBuilder: " +
"PREAUTH FAILED/REQ, re-send AS-REQ");
}
preAuthFailedOnce = true;
KRBError kerr = ke.getError();
int paEType = PAData.getPreferredEType(kerr.getPA(),
EType.getDefaults("default_tkt_enctypes")[0]);
if (password == null) {
EncryptionKey[] ks = Krb5Util.keysFromJavaxKeyTab(ktab, cname);
pakey = EncryptionKey.findKey(paEType, ks);
if (pakey != null) pakey = (EncryptionKey)pakey.clone();
for (EncryptionKey k: ks) k.destroy();
} else {
pakey = EncryptionKey.acquireSecretKey(cname,
password,
paEType,
PAData.getSaltAndParams(
paEType, kerr.getPA()));
}
paList = kerr.getPA(); // Update current paList
} else {
throw ke;
}
}
}
}
/**
* Performs AS-REQ send and AS-REP receive.
* Maybe a state is needed here, to divide prepare process and getCreds.
* @throws KrbException
* @throws Asn1Exception
* @throws IOException
*/
public KrbAsReqBuilder action()
throws KrbException, Asn1Exception, IOException {
checkState(State.INIT, "Cannot call action");
state = State.REQ_OK;
return send().resolve();
}
/**
* Gets Credentials object after action
*/
public Credentials getCreds() {
checkState(State.REQ_OK, "Cannot retrieve creds");
return rep.getCreds();
}
/**
* Gets another type of Credentials after action
*/
public sun.security.krb5.internal.ccache.Credentials getCCreds() {
checkState(State.REQ_OK, "Cannot retrieve CCreds");
return rep.getCCreds();
}
/**
* Destroys the object and clears keys and password info.
*/
public void destroy() {
state = State.DESTROYED;
if (password != null) {
Arrays.fill(password, (char)0);
}
}
/**
* Checks if the current state is the specified one.
* @param st the expected state
* @param msg error message if state is not correct
* @throws IllegalStateException if state is not correct
*/
private void checkState(State st, String msg) {
if (state != st) {
throw new IllegalStateException(msg + " at " + st + " state");
}
}
}

View file

@ -0,0 +1,174 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.KeyUsage;
import java.io.IOException;
import sun.security.util.DerValue;
/**
* This class encapsulates the KRB-CRED message that a client uses to
* send its delegated credentials to a server.
*
* Supports delegation of one ticket only.
* @author Mayank Upadhyay
*/
public class KrbCred {
private static boolean DEBUG = Krb5.DEBUG;
private byte[] obuf = null;
private KRBCred credMessg = null;
private Ticket ticket = null;
private EncKrbCredPart encPart = null;
private Credentials creds = null;
private KerberosTime timeStamp = null;
// Used in InitialToken with null key
public KrbCred(Credentials tgt,
Credentials serviceTicket,
EncryptionKey key)
throws KrbException, IOException {
PrincipalName client = tgt.getClient();
PrincipalName tgService = tgt.getServer();
if (!serviceTicket.getClient().equals(client))
throw new KrbException(Krb5.KRB_ERR_GENERIC,
"Client principal does not match");
// XXX Check Windows flag OK-TO-FORWARD-TO
// Invoke TGS-REQ to get a forwarded TGT for the peer
KDCOptions options = new KDCOptions();
options.set(KDCOptions.FORWARDED, true);
options.set(KDCOptions.FORWARDABLE, true);
KrbTgsReq tgsReq = new KrbTgsReq(options, tgt, tgService,
null, null, null, null,
null, // No easy way to get addresses right
null, null, null);
credMessg = createMessage(tgsReq.sendAndGetCreds(), key);
obuf = credMessg.asn1Encode();
}
KRBCred createMessage(Credentials delegatedCreds, EncryptionKey key)
throws KrbException, IOException {
EncryptionKey sessionKey
= delegatedCreds.getSessionKey();
PrincipalName princ = delegatedCreds.getClient();
PrincipalName tgService = delegatedCreds.getServer();
KrbCredInfo credInfo = new KrbCredInfo(sessionKey,
princ, delegatedCreds.flags, delegatedCreds.authTime,
delegatedCreds.startTime, delegatedCreds.endTime,
delegatedCreds.renewTill, tgService,
delegatedCreds.cAddr);
timeStamp = KerberosTime.now();
KrbCredInfo[] credInfos = {credInfo};
EncKrbCredPart encPart =
new EncKrbCredPart(credInfos,
timeStamp, null, null, null, null);
EncryptedData encEncPart = new EncryptedData(key,
encPart.asn1Encode(), KeyUsage.KU_ENC_KRB_CRED_PART);
Ticket[] tickets = {delegatedCreds.ticket};
credMessg = new KRBCred(tickets, encEncPart);
return credMessg;
}
// Used in InitialToken, NULL_KEY might be used
public KrbCred(byte[] asn1Message, EncryptionKey key)
throws KrbException, IOException {
credMessg = new KRBCred(asn1Message);
ticket = credMessg.tickets[0];
if (credMessg.encPart.getEType() == 0) {
key = EncryptionKey.NULL_KEY;
}
byte[] temp = credMessg.encPart.decrypt(key,
KeyUsage.KU_ENC_KRB_CRED_PART);
byte[] plainText = credMessg.encPart.reset(temp);
DerValue encoding = new DerValue(plainText);
EncKrbCredPart encPart = new EncKrbCredPart(encoding);
timeStamp = encPart.timeStamp;
KrbCredInfo credInfo = encPart.ticketInfo[0];
EncryptionKey credInfoKey = credInfo.key;
PrincipalName pname = credInfo.pname;
TicketFlags flags = credInfo.flags;
KerberosTime authtime = credInfo.authtime;
KerberosTime starttime = credInfo.starttime;
KerberosTime endtime = credInfo.endtime;
KerberosTime renewTill = credInfo.renewTill;
PrincipalName sname = credInfo.sname;
HostAddresses caddr = credInfo.caddr;
if (DEBUG) {
System.out.println(">>>Delegated Creds have pname=" + pname
+ " sname=" + sname
+ " authtime=" + authtime
+ " starttime=" + starttime
+ " endtime=" + endtime
+ "renewTill=" + renewTill);
}
creds = new Credentials(ticket, pname, sname, credInfoKey,
flags, authtime, starttime, endtime, renewTill, caddr);
}
/**
* Returns the delegated credentials from the peer.
*/
public Credentials[] getDelegatedCreds() {
Credentials[] allCreds = {creds};
return allCreds;
}
/**
* Returns the ASN.1 encoding that should be sent to the peer.
*/
public byte[] getMessage() {
return obuf;
}
}

View file

@ -0,0 +1,45 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
/**
* KrbCryptoExceptoin is a wrapper exception for exceptions thrown by JCE.
*
* @author Yanni Zhang
*/
public class KrbCryptoException extends KrbException {
private static final long serialVersionUID = -1657367919979982250L;
public KrbCryptoException (String s) {
super(s);
}
}

View file

@ -0,0 +1,160 @@
/*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.Krb5;
import sun.security.krb5.internal.KRBError;
public class KrbException extends Exception {
private static final long serialVersionUID = -4993302876451928596L;
private int returnCode;
private KRBError error;
public KrbException(String s) {
super(s);
}
public KrbException(Throwable cause) {
super(cause);
}
public KrbException(int i) {
returnCode = i;
}
public KrbException(int i, String s) {
this(s);
returnCode = i;
}
public KrbException(KRBError e) {
returnCode = e.getErrorCode();
error = e;
}
public KrbException(KRBError e, String s) {
this(s);
returnCode = e.getErrorCode();
error = e;
}
public KRBError getError() {
return error;
}
public int returnCode() {
return returnCode;
}
public String returnCodeSymbol() {
return returnCodeSymbol(returnCode);
}
public static String returnCodeSymbol(int i) {
return "not yet implemented";
}
public String returnCodeMessage() {
return Krb5.getErrorMessage(returnCode);
}
public static String errorMessage(int i) {
return Krb5.getErrorMessage(i);
}
public String krbErrorMessage() {
StringBuilder sb = new StringBuilder();
sb.append("krb_error ").append(returnCode);
String msg = getMessage();
if (msg != null) {
sb.append(" ");
sb.append(msg);
}
return sb.toString();
}
/**
* Returns messages like:
* "Integrity check on decrypted field failed (31) - \
* Could not decrypt service ticket"
* If the error code is 0 then the first half is skipped.
*/
public String getMessage() {
StringBuilder message = new StringBuilder();
int returnCode = returnCode();
if (returnCode != 0) {
message.append(returnCodeMessage());
message.append(" (").append(returnCode()).append(')');
}
String consMessage = super.getMessage();
if (consMessage != null && consMessage.length() != 0) {
if (returnCode != 0)
message.append(" - ");
message.append(consMessage);
}
return message.toString();
}
public String toString() {
return ("KrbException: " + getMessage());
}
@Override public int hashCode() {
int result = 17;
result = 37 * result + returnCode;
if (error != null) {
result = 37 * result + error.hashCode();
}
return result;
}
@Override public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof KrbException)) {
return false;
}
KrbException other = (KrbException)obj;
if (returnCode != other.returnCode) {
return false;
}
return (error == null)?(other.error == null):
(error.equals(other.error));
}
}

View file

@ -0,0 +1,122 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
abstract class KrbKdcRep {
static void check(
boolean isAsReq,
KDCReq req,
KDCRep rep
) throws KrbApErrException {
if (isAsReq && !req.reqBody.cname.equals(rep.cname)) {
rep.encKDCRepPart.key.destroy();
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
if (!req.reqBody.sname.equals(rep.encKDCRepPart.sname)) {
rep.encKDCRepPart.key.destroy();
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
if (req.reqBody.getNonce() != rep.encKDCRepPart.nonce) {
rep.encKDCRepPart.key.destroy();
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
if (
((req.reqBody.addresses != null && rep.encKDCRepPart.caddr != null) &&
!req.reqBody.addresses.equals(rep.encKDCRepPart.caddr))) {
rep.encKDCRepPart.key.destroy();
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
// We allow KDC to return a non-forwardable ticket if request has -f
for (int i = 2; i < 6; i++) {
if (req.reqBody.kdcOptions.get(i) !=
rep.encKDCRepPart.flags.get(i)) {
if (Krb5.DEBUG) {
System.out.println("> KrbKdcRep.check: at #" + i
+ ". request for " + req.reqBody.kdcOptions.get(i)
+ ", received " + rep.encKDCRepPart.flags.get(i));
}
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
}
// Reply to a renewable request should be renewable, but if request does
// not contain renewable, KDC is free to issue a renewable ticket (for
// example, if ticket_lifetime is too big).
if (req.reqBody.kdcOptions.get(KDCOptions.RENEWABLE) &&
!rep.encKDCRepPart.flags.get(KDCOptions.RENEWABLE)) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
if ((req.reqBody.from == null) || req.reqBody.from.isZero()) {
// verify this is allowed
if ((rep.encKDCRepPart.starttime != null) &&
!rep.encKDCRepPart.starttime.inClockSkew()) {
rep.encKDCRepPart.key.destroy();
throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW);
}
}
if ((req.reqBody.from != null) && !req.reqBody.from.isZero()) {
// verify this is allowed
if ((rep.encKDCRepPart.starttime != null) &&
!req.reqBody.from.equals(rep.encKDCRepPart.starttime)) {
rep.encKDCRepPart.key.destroy();
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
}
if (!req.reqBody.till.isZero() &&
rep.encKDCRepPart.endtime.greaterThan(req.reqBody.till)) {
rep.encKDCRepPart.key.destroy();
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
if (req.reqBody.kdcOptions.get(KDCOptions.RENEWABLE)) {
if (req.reqBody.rtime != null && !req.reqBody.rtime.isZero()) {
// verify this is required
if ((rep.encKDCRepPart.renewTill == null) ||
rep.encKDCRepPart.renewTill.greaterThan(req.reqBody.rtime)
) {
rep.encKDCRepPart.key.destroy();
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
}
}
}
}

View file

@ -0,0 +1,178 @@
/*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
import sun.security.util.*;
import java.io.IOException;
/** XXX This class does not appear to be used. **/
class KrbPriv extends KrbAppMessage {
private byte[] obuf;
private byte[] userData;
private KrbPriv(byte[] userData,
Credentials creds,
EncryptionKey subKey,
KerberosTime timestamp,
SeqNumber seqNumber,
HostAddress saddr,
HostAddress raddr
) throws KrbException, IOException {
EncryptionKey reqKey = null;
if (subKey != null)
reqKey = subKey;
else
reqKey = creds.key;
obuf = mk_priv(
userData,
reqKey,
timestamp,
seqNumber,
saddr,
raddr
);
}
private KrbPriv(byte[] msg,
Credentials creds,
EncryptionKey subKey,
SeqNumber seqNumber,
HostAddress saddr,
HostAddress raddr,
boolean timestampRequired,
boolean seqNumberRequired
) throws KrbException, IOException {
KRBPriv krb_priv = new KRBPriv(msg);
EncryptionKey reqKey = null;
if (subKey != null)
reqKey = subKey;
else
reqKey = creds.key;
userData = rd_priv(krb_priv,
reqKey,
seqNumber,
saddr,
raddr,
timestampRequired,
seqNumberRequired,
creds.client
);
}
public byte[] getMessage() throws KrbException {
return obuf;
}
public byte[] getData() {
return userData;
}
private byte[] mk_priv(byte[] userData,
EncryptionKey key,
KerberosTime timestamp,
SeqNumber seqNumber,
HostAddress sAddress,
HostAddress rAddress
) throws Asn1Exception, IOException,
KdcErrException, KrbCryptoException {
Integer usec = null;
Integer seqno = null;
if (timestamp != null)
usec = timestamp.getMicroSeconds();
if (seqNumber != null) {
seqno = seqNumber.current();
seqNumber.step();
}
EncKrbPrivPart unenc_encKrbPrivPart =
new EncKrbPrivPart(userData,
timestamp,
usec,
seqno,
sAddress,
rAddress
);
byte[] temp = unenc_encKrbPrivPart.asn1Encode();
EncryptedData encKrbPrivPart =
new EncryptedData(key, temp,
KeyUsage.KU_ENC_KRB_PRIV_PART);
KRBPriv krb_priv = new KRBPriv(encKrbPrivPart);
temp = krb_priv.asn1Encode();
return krb_priv.asn1Encode();
}
private byte[] rd_priv(KRBPriv krb_priv,
EncryptionKey key,
SeqNumber seqNumber,
HostAddress sAddress,
HostAddress rAddress,
boolean timestampRequired,
boolean seqNumberRequired,
PrincipalName cname
) throws Asn1Exception, KdcErrException,
KrbApErrException, IOException, KrbCryptoException {
byte[] bytes = krb_priv.encPart.decrypt(key,
KeyUsage.KU_ENC_KRB_PRIV_PART);
byte[] temp = krb_priv.encPart.reset(bytes);
DerValue ref = new DerValue(temp);
EncKrbPrivPart enc_part = new EncKrbPrivPart(ref);
check(enc_part.timestamp,
enc_part.usec,
enc_part.seqNumber,
enc_part.sAddress,
enc_part.rAddress,
seqNumber,
sAddress,
rAddress,
timestampRequired,
seqNumberRequired,
cname
);
return enc_part.userData;
}
}

View file

@ -0,0 +1,183 @@
/*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.EncryptionKey;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
import java.io.IOException;
class KrbSafe extends KrbAppMessage {
private byte[] obuf;
private byte[] userData;
public KrbSafe(byte[] userData,
Credentials creds,
EncryptionKey subKey,
KerberosTime timestamp,
SeqNumber seqNumber,
HostAddress saddr,
HostAddress raddr
) throws KrbException, IOException {
EncryptionKey reqKey = null;
if (subKey != null)
reqKey = subKey;
else
reqKey = creds.key;
obuf = mk_safe(userData,
reqKey,
timestamp,
seqNumber,
saddr,
raddr
);
}
public KrbSafe(byte[] msg,
Credentials creds,
EncryptionKey subKey,
SeqNumber seqNumber,
HostAddress saddr,
HostAddress raddr,
boolean timestampRequired,
boolean seqNumberRequired
) throws KrbException, IOException {
KRBSafe krb_safe = new KRBSafe(msg);
EncryptionKey reqKey = null;
if (subKey != null)
reqKey = subKey;
else
reqKey = creds.key;
userData = rd_safe(
krb_safe,
reqKey,
seqNumber,
saddr,
raddr,
timestampRequired,
seqNumberRequired,
creds.client
);
}
public byte[] getMessage() {
return obuf;
}
public byte[] getData() {
return userData;
}
private byte[] mk_safe(byte[] userData,
EncryptionKey key,
KerberosTime timestamp,
SeqNumber seqNumber,
HostAddress sAddress,
HostAddress rAddress
) throws Asn1Exception, IOException, KdcErrException,
KrbApErrException, KrbCryptoException {
Integer usec = null;
Integer seqno = null;
if (timestamp != null)
usec = timestamp.getMicroSeconds();
if (seqNumber != null) {
seqno = seqNumber.current();
seqNumber.step();
}
KRBSafeBody krb_safeBody =
new KRBSafeBody(userData,
timestamp,
usec,
seqno,
sAddress,
rAddress
);
byte[] temp = krb_safeBody.asn1Encode();
Checksum cksum = new Checksum(
Checksum.SAFECKSUMTYPE_DEFAULT,
temp,
key,
KeyUsage.KU_KRB_SAFE_CKSUM
);
KRBSafe krb_safe = new KRBSafe(krb_safeBody, cksum);
temp = krb_safe.asn1Encode();
return krb_safe.asn1Encode();
}
private byte[] rd_safe(KRBSafe krb_safe,
EncryptionKey key,
SeqNumber seqNumber,
HostAddress sAddress,
HostAddress rAddress,
boolean timestampRequired,
boolean seqNumberRequired,
PrincipalName cname
) throws Asn1Exception, KdcErrException,
KrbApErrException, IOException, KrbCryptoException {
byte[] temp = krb_safe.safeBody.asn1Encode();
if (!krb_safe.cksum.verifyKeyedChecksum(temp, key,
KeyUsage.KU_KRB_SAFE_CKSUM)) {
throw new KrbApErrException(
Krb5.KRB_AP_ERR_MODIFIED);
}
check(krb_safe.safeBody.timestamp,
krb_safe.safeBody.usec,
krb_safe.safeBody.seqNumber,
krb_safe.safeBody.sAddress,
krb_safe.safeBody.rAddress,
seqNumber,
sAddress,
rAddress,
timestampRequired,
seqNumberRequired,
cname
);
return krb_safe.safeBody.userData;
}
}

Some files were not shown because too many files have changed in this diff Show more