From 0b477093433be9f731dfad40e8e3d7d7d8b71a73 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Tue, 10 Nov 2015 21:32:45 +0000 Subject: [PATCH 01/25] Add missing dbgsysGetLastErrorString() --- .../share/native/libjdwp/export/sys.h | 1 + .../unix/native/libjdwp/linker_md.c | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/export/sys.h b/src/jdk.jdwp.agent/share/native/libjdwp/export/sys.h index 9561112457d..1caae50c679 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/export/sys.h +++ b/src/jdk.jdwp.agent/share/native/libjdwp/export/sys.h @@ -41,6 +41,7 @@ void dbgsysBuildLibName(char *, int, const char *, const char *); void * dbgsysLoadLibrary(const char *, char *err_buf, int err_buflen); void dbgsysUnloadLibrary(void *); void * dbgsysFindLibraryEntry(void *, const char *); +int dbgsysGetLastErrorString(char *, int); /* Implemented in exec_md.c */ int dbgsysExec(char *cmdLine); diff --git a/src/jdk.jdwp.agent/unix/native/libjdwp/linker_md.c b/src/jdk.jdwp.agent/unix/native/libjdwp/linker_md.c index 47b6d74dfa7..0b69d62215b 100644 --- a/src/jdk.jdwp.agent/unix/native/libjdwp/linker_md.c +++ b/src/jdk.jdwp.agent/unix/native/libjdwp/linker_md.c @@ -78,6 +78,28 @@ static void dll_build_name(char* buffer, size_t buflen, free(paths_copy); } +int +dbgsysGetLastErrorString(char *buf, int len) +{ + const char *s = dlerror(); + size_t n; + size_t l = (size_t)len; + + if (len <= 0) + return 0; + + *buf = '\0'; + if (s == NULL) + return 0; + + n = strlen(s); + if (n >= l) + n = l - 1; + strncpy(buf, s, n); + buf[n] = '\0'; /* not actually needed */ + return n; +} + /* * create a string for the JNI native function name by adding the * appropriate decorations. From e7b31c270761d58b3a375814bfcaf81033aa5e3f Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Thu, 20 Sep 2018 18:16:41 -0500 Subject: [PATCH 02/25] JGSS: Fix cut/paste error in NativeUtil.c --- src/java.security.jgss/share/native/libj2gss/NativeUtil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.security.jgss/share/native/libj2gss/NativeUtil.c b/src/java.security.jgss/share/native/libj2gss/NativeUtil.c index 8e40177babf..3fba9fcdbfa 100644 --- a/src/java.security.jgss/share/native/libj2gss/NativeUtil.c +++ b/src/java.security.jgss/share/native/libj2gss/NativeUtil.c @@ -145,7 +145,7 @@ DEF_JNI_OnLoad(JavaVM *jvm, void *reserved) { return JNI_ERR; } CLS_GSSNameElement = (*env)->NewGlobalRef(env, cls); - if (CLS_GSSException == NULL) { + if (CLS_GSSNameElement == NULL) { return JNI_ERR; } cls = (*env)->FindClass(env, "sun/security/jgss/wrapper/GSSCredElement"); From 003d82b4c249bb405c0a09bc3c1ce03ff4ad4a57 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Fri, 21 Sep 2018 14:41:56 -0500 Subject: [PATCH 03/25] Fix error handling in GSSLibStub Also improve object size handling in NativeUtil. --- .../share/native/libj2gss/GSSLibStub.c | 187 ++++++++++-------- .../share/native/libj2gss/NativeUtil.c | 87 ++++---- .../share/native/libj2gss/NativeUtil.h | 4 +- 3 files changed, 153 insertions(+), 125 deletions(-) diff --git a/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c b/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c index acb9a914f1a..3f131876ab1 100644 --- a/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c +++ b/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c @@ -472,10 +472,9 @@ JNIEXPORT jbyteArray JNICALL Java_sun_security_jgss_wrapper_GSSLibStub_exportName(JNIEnv *env, jobject jobj, jlong pName) { - OM_uint32 minor, major; + OM_uint32 minor, major, dummy; gss_name_t nameHdl, mNameHdl; - gss_buffer_desc outBuf; - jbyteArray jresult; + gss_buffer_desc outBuf = GSS_C_EMPTY_BUFFER; nameHdl = (gss_name_t) jlong_to_ptr(pName); @@ -500,26 +499,16 @@ Java_sun_security_jgss_wrapper_GSSLibStub_exportName(JNIEnv *env, } major = (*ftab->exportName)(&minor, mNameHdl, &outBuf); - Java_sun_security_jgss_wrapper_GSSLibStub_releaseName - (env, jobj, ptr_to_jlong(mNameHdl)); - if ((*env)->ExceptionCheck(env)) { - /* release intermediate buffers */ - (*ftab->releaseBuffer)(&minor, &outBuf); - return NULL; - } - } - - /* release intermediate buffers before checking status */ - jresult = getJavaBuffer(env, &outBuf); - if ((*env)->ExceptionCheck(env)) { - return NULL; + (void) (*ftab->releaseName)(&dummy, &mNameHdl); } checkStatus(env, jobj, major, minor, "[GSSLibStub_exportName]"); if ((*env)->ExceptionCheck(env)) { + (void) (*ftab->releaseBuffer)(&dummy, &outBuf); return NULL; } - return jresult; + /* Map outBuf to byteArray result and release */ + return getJavaBuffer(env, &outBuf, JNI_TRUE); } /* @@ -533,7 +522,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_displayName(JNIEnv *env, jlong pName) { OM_uint32 minor, major; gss_name_t nameHdl; - gss_buffer_desc outNameBuf; + gss_buffer_desc outNameBuf = GSS_C_EMPTY_BUFFER; gss_OID outNameType; jstring jname; jobject jtype; @@ -854,7 +843,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_initContext(JNIEnv *env, jbyteArray jinToken, jobject jcontextSpi) { - OM_uint32 minor, major; + OM_uint32 minor, major, dummy; gss_cred_id_t credHdl ; gss_ctx_id_t contextHdl, contextHdlSave; gss_name_t targetName; @@ -863,8 +852,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_initContext(JNIEnv *env, OM_uint32 time, aTime; gss_channel_bindings_t cb; gss_buffer_desc inToken; - gss_buffer_desc outToken; - jbyteArray jresult; + gss_buffer_desc outToken = GSS_C_EMPTY_BUFFER; /* UNCOMMENT after SEAM bug#6287358 is backported to S10 gss_OID aMech; jobject jMech; @@ -938,23 +926,21 @@ Java_sun_security_jgss_wrapper_GSSLibStub_initContext(JNIEnv *env, */ } else if (major & GSS_S_CONTINUE_NEEDED) { TRACE0("[GSSLibStub_initContext] context not established"); - major -= GSS_S_CONTINUE_NEEDED; + major &= ~GSS_S_CONTINUE_NEEDED; } } /* release intermediate buffers before checking status */ deleteGSSCB(cb); resetGSSBuffer(&inToken); - jresult = getJavaBuffer(env, &outToken); - if ((*env)->ExceptionCheck(env)) { - return NULL; - } checkStatus(env, jobj, major, minor, "[GSSLibStub_initContext]"); if ((*env)->ExceptionCheck(env)) { + (void) (*ftab->releaseBuffer)(&dummy, &outToken); return NULL; } - return jresult; + /* Map outToken to byteArray result and release */ + return getJavaBuffer(env, &outToken, JNI_TRUE); } /* @@ -970,23 +956,23 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env, jbyteArray jinToken, jobject jcontextSpi) { - OM_uint32 minor, major; + OM_uint32 minor, major, dummy; OM_uint32 minor2, major2; gss_ctx_id_t contextHdl, contextHdlSave; gss_cred_id_t credHdl; gss_buffer_desc inToken; gss_channel_bindings_t cb; - gss_name_t srcName; - gss_buffer_desc outToken; + gss_name_t srcName = GSS_C_NO_NAME; + gss_buffer_desc outToken = GSS_C_EMPTY_BUFFER; gss_OID aMech; OM_uint32 aFlags; OM_uint32 aTime; - gss_cred_id_t delCred; - jobject jsrcName=GSS_C_NO_NAME; + gss_cred_id_t delCred = GSS_C_NO_CREDENTIAL; + jobject jsrcName = NULL; jobject jdelCred; - jobject jMech; + jobject jMech = NULL; jboolean setTarget; - gss_name_t targetName; + gss_name_t targetName = GSS_C_NO_NAME; jobject jtargetName; TRACE0("[GSSLibStub_acceptContext]"); @@ -1003,8 +989,6 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env, resetGSSBuffer(&inToken); return NULL; } - srcName = targetName = GSS_C_NO_NAME; - delCred = GSS_C_NO_CREDENTIAL; setTarget = (credHdl == GSS_C_NO_CREDENTIAL); aFlags = 0; @@ -1039,9 +1023,9 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env, if (GSS_ERROR(major) == GSS_S_COMPLETE) { /* update member values if needed */ - // WORKAROUND for a Heimdal bug + // WORKAROUND for an old Heimdal bug if (delCred == GSS_C_NO_CREDENTIAL) { - aFlags &= 0xfffffffe; + aFlags &= ~GSS_C_DELEG_FLAG; } (*env)->SetIntField(env, jcontextSpi, FID_NativeGSSContext_flags, aFlags); TRACE1("[GSSLibStub_acceptContext] set flags=0x%x", aFlags); @@ -1065,6 +1049,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env, TRACE1("[GSSLibStub_acceptContext] set targetName=%" PRIuPTR "", (uintptr_t)targetName); + targetName = GSS_C_NO_NAME; (*env)->SetObjectField(env, jcontextSpi, FID_NativeGSSContext_targetName, jtargetName); @@ -1081,6 +1066,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env, } TRACE1("[GSSLibStub_acceptContext] set srcName=%" PRIuPTR "", (uintptr_t)srcName); + srcName = GSS_C_NO_NAME; (*env)->SetObjectField(env, jcontextSpi, FID_NativeGSSContext_srcName, jsrcName); @@ -1112,11 +1098,12 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env, if ((*env)->ExceptionCheck(env)) { goto error; } + TRACE1("[GSSLibStub_acceptContext] set delegatedCred=%" PRIuPTR "", + (uintptr_t) delCred); + delCred = GSS_C_NO_CREDENTIAL; (*env)->SetObjectField(env, jcontextSpi, FID_NativeGSSContext_delegatedCred, jdelCred); - TRACE1("[GSSLibStub_acceptContext] set delegatedCred=%" PRIuPTR "", - (uintptr_t) delCred); if ((*env)->ExceptionCheck(env)) { goto error; @@ -1129,10 +1116,18 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env, (*env)->SetIntField(env, jcontextSpi, FID_NativeGSSContext_lifetime, getJavaTime(aTime)); } - major -= GSS_S_CONTINUE_NEEDED; + major &= ~GSS_S_CONTINUE_NEEDED; } } - return getJavaBuffer(env, &outToken); + + checkStatus(env, jobj, major, minor, "[GSSLibStub_acceptContext]"); + if ((*env)->ExceptionCheck(env)) { + (void) (*ftab->releaseBuffer)(&dummy, &outToken); + return NULL; + } + + /* Map outToken to byteArray result and release */ + return getJavaBuffer(env, &outToken, JNI_TRUE); error: (*ftab->releaseBuffer)(&minor, &outToken); @@ -1158,12 +1153,14 @@ Java_sun_security_jgss_wrapper_GSSLibStub_inquireContext(JNIEnv *env, jobject jobj, jlong pContext) { - OM_uint32 minor, major; + OM_uint32 minor, major, dummy; gss_ctx_id_t contextHdl; - gss_name_t srcName, targetName; - OM_uint32 time; - OM_uint32 flags; - int isInitiator, isEstablished; + gss_name_t srcName = GSS_C_NO_NAME; + gss_name_t targetName = GSS_C_NO_NAME; + OM_uint32 time = 0; + OM_uint32 flags = 0; + int isInitiator = 0; + int isEstablished = 0; jlong result[6]; jlongArray jresult; @@ -1171,10 +1168,6 @@ Java_sun_security_jgss_wrapper_GSSLibStub_inquireContext(JNIEnv *env, TRACE1("[GSSLibStub_inquireContext] %" PRIuPTR "", (uintptr_t)contextHdl); - srcName = targetName = GSS_C_NO_NAME; - time = 0; - flags = isInitiator = isEstablished = 0; - /* gss_inquire_context(...) => GSS_S_NO_CONTEXT(!) */ major = (*ftab->inquireContext)(&minor, contextHdl, &srcName, &targetName, &time, NULL, &flags, @@ -1185,6 +1178,8 @@ Java_sun_security_jgss_wrapper_GSSLibStub_inquireContext(JNIEnv *env, checkStatus(env, jobj, major, minor, "[GSSLibStub_inquireContext]"); if ((*env)->ExceptionCheck(env)) { + (void) (*ftab->releaseName)(&dummy, &srcName); + (void) (*ftab->releaseName)(&dummy, &targetName); return NULL; } result[0] = ptr_to_jlong(srcName); @@ -1195,11 +1190,12 @@ Java_sun_security_jgss_wrapper_GSSLibStub_inquireContext(JNIEnv *env, result[5] = (jlong) getJavaTime(time); jresult = (*env)->NewLongArray(env, 6); - if (jresult == NULL) { - return NULL; + if (jresult != NULL) { + (*env)->SetLongArrayRegion(env, jresult, 0, 6, result); } - (*env)->SetLongArrayRegion(env, jresult, 0, 6, result); if ((*env)->ExceptionCheck(env)) { + (void) (*ftab->releaseName)(&dummy, &srcName); + (void) (*ftab->releaseName)(&dummy, &targetName); return NULL; } return jresult; @@ -1245,7 +1241,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_getContextName(JNIEnv *env, jobject jobj, jlong pContext, jboolean isSrc) { OM_uint32 minor, major; - gss_name_t nameHdl; + gss_name_t nameHdl = GSS_C_NO_NAME; gss_ctx_id_t contextHdl; contextHdl = (gss_ctx_id_t) jlong_to_ptr(pContext); @@ -1253,7 +1249,6 @@ Java_sun_security_jgss_wrapper_GSSLibStub_getContextName(JNIEnv *env, TRACE2("[GSSLibStub_getContextName] %" PRIuPTR ", isSrc=%d", (uintptr_t)contextHdl, isSrc); - nameHdl = GSS_C_NO_NAME; if (isSrc == JNI_TRUE) { major = (*ftab->inquireContext)(&minor, contextHdl, &nameHdl, NULL, NULL, NULL, NULL, NULL, NULL); @@ -1351,6 +1346,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_wrapSizeLimit(JNIEnv *env, gss_ctx_id_t contextHdl; OM_uint32 outSize, maxInSize; gss_qop_t qop; + jint result; contextHdl = (gss_ctx_id_t) jlong_to_ptr(pContext); @@ -1374,7 +1370,14 @@ Java_sun_security_jgss_wrapper_GSSLibStub_wrapSizeLimit(JNIEnv *env, if ((*env)->ExceptionCheck(env)) { return 0; } - return (jint) maxInSize; + + /* Right-shift maxInSize until it fits into jint */ + result = (jint)maxInSize; + while (result < 0 || maxInSize != (OM_uint32)result) { + result = (jint)(maxInSize >>= 1); + } + + return result; } /* @@ -1387,10 +1390,9 @@ Java_sun_security_jgss_wrapper_GSSLibStub_exportContext(JNIEnv *env, jobject jobj, jlong pContext) { - OM_uint32 minor, major; + OM_uint32 minor, major, dummy; gss_ctx_id_t contextHdl; - gss_buffer_desc interProcToken; - jbyteArray jresult; + gss_buffer_desc interProcToken = GSS_C_EMPTY_BUFFER; contextHdl = (gss_ctx_id_t) jlong_to_ptr(pContext); @@ -1406,17 +1408,14 @@ Java_sun_security_jgss_wrapper_GSSLibStub_exportContext(JNIEnv *env, major = (*ftab->exportSecContext)(&minor, &contextHdl, &interProcToken); - /* release intermediate buffers */ - jresult = getJavaBuffer(env, &interProcToken); - if ((*env)->ExceptionCheck(env)) { - return NULL; - } checkStatus(env, jobj, major, minor, "[GSSLibStub_exportContext]"); if ((*env)->ExceptionCheck(env)) { + (void) (*ftab->releaseBuffer)(&dummy, &interProcToken); return NULL; } - return jresult; + /* Map interProcToken to byteArray result and release */ + return getJavaBuffer(env, &interProcToken, JNI_TRUE); } /* @@ -1429,12 +1428,11 @@ Java_sun_security_jgss_wrapper_GSSLibStub_getMic(JNIEnv *env, jobject jobj, jlong pContext, jint jqop, jbyteArray jmsg) { - OM_uint32 minor, major; + OM_uint32 minor, major, dummy; gss_ctx_id_t contextHdl; gss_qop_t qop; gss_buffer_desc msg; - gss_buffer_desc msgToken; - jbyteArray jresult; + gss_buffer_desc msgToken = GSS_C_EMPTY_BUFFER; contextHdl = (gss_ctx_id_t) jlong_to_ptr(pContext); @@ -1458,16 +1456,14 @@ Java_sun_security_jgss_wrapper_GSSLibStub_getMic(JNIEnv *env, jobject jobj, /* release intermediate buffers */ resetGSSBuffer(&msg); - jresult = getJavaBuffer(env, &msgToken); - if ((*env)->ExceptionCheck(env)) { - return NULL; - } checkStatus(env, jobj, major, minor, "[GSSLibStub_getMic]"); if ((*env)->ExceptionCheck(env)) { + (void) (*ftab->releaseBuffer)(&dummy, &msgToken); return NULL; } - return jresult; + /* Map msgToken to byteArray result and release */ + return getJavaBuffer(env, &msgToken, JNI_TRUE); } /* @@ -1522,6 +1518,11 @@ Java_sun_security_jgss_wrapper_GSSLibStub_verifyMic(JNIEnv *env, resetGSSBuffer(&msg); resetGSSBuffer(&msgToken); + /* + * We don't throw on supplementary status codes here, instead we pass only + * GSS_ERROR(major) to checkStatus() and set the supplementary status in the + * message properties. + */ checkStatus(env, jobj, GSS_ERROR(major), minor, "[GSSLibStub_verifyMic]"); if ((*env)->ExceptionCheck(env)) { return; @@ -1534,9 +1535,6 @@ Java_sun_security_jgss_wrapper_GSSLibStub_verifyMic(JNIEnv *env, setSupplementaryInfo(env, jobj, jprop, GSS_SUPPLEMENTARY_INFO(major), minor); - if ((*env)->ExceptionCheck(env)) { - return; - } } /* @@ -1551,11 +1549,11 @@ Java_sun_security_jgss_wrapper_GSSLibStub_wrap(JNIEnv *env, jbyteArray jmsg, jobject jprop) { - OM_uint32 minor, major; + OM_uint32 minor, major, dummy; jboolean confFlag; gss_qop_t qop; gss_buffer_desc msg; - gss_buffer_desc msgToken; + gss_buffer_desc msgToken = GSS_C_EMPTY_BUFFER; int confState; gss_ctx_id_t contextHdl; jbyteArray jresult; @@ -1594,12 +1592,14 @@ Java_sun_security_jgss_wrapper_GSSLibStub_wrap(JNIEnv *env, /* release intermediate buffers */ resetGSSBuffer(&msg); - jresult = getJavaBuffer(env, &msgToken); + checkStatus(env, jobj, major, minor, "[GSSLibStub_wrap]"); if ((*env)->ExceptionCheck(env)) { + (void) (*ftab->releaseBuffer)(&dummy, &msgToken); return NULL; } - checkStatus(env, jobj, major, minor, "[GSSLibStub_wrap]"); + /* Map msgToken to byteArray result and release */ + jresult = getJavaBuffer(env, &msgToken, JNI_TRUE); if ((*env)->ExceptionCheck(env)) { return NULL; } @@ -1607,7 +1607,8 @@ Java_sun_security_jgss_wrapper_GSSLibStub_wrap(JNIEnv *env, (*env)->CallVoidMethod(env, jprop, MID_MessageProp_setPrivacy, (confState? JNI_TRUE:JNI_FALSE)); if ((*env)->ExceptionCheck(env)) { - return NULL; + (*env)->DeleteLocalRef(env, jresult); + jresult = NULL; } return jresult; } @@ -1624,10 +1625,10 @@ Java_sun_security_jgss_wrapper_GSSLibStub_unwrap(JNIEnv *env, jbyteArray jmsgToken, jobject jprop) { - OM_uint32 minor, major; + OM_uint32 minor, major, dummy; gss_ctx_id_t contextHdl; gss_buffer_desc msgToken; - gss_buffer_desc msg; + gss_buffer_desc msg = GSS_C_EMPTY_BUFFER; int confState; gss_qop_t qop; jbyteArray jresult; @@ -1657,12 +1658,23 @@ Java_sun_security_jgss_wrapper_GSSLibStub_unwrap(JNIEnv *env, /* release intermediate buffers */ resetGSSBuffer(&msgToken); - jresult = getJavaBuffer(env, &msg); + + /* + * We don't throw on supplementary status codes here, instead we pass only + * GSS_ERROR(major) to checkStatus() and set the supplementary status in the + * message properties. + */ + checkStatus(env, jobj, GSS_ERROR(major), minor, "[GSSLibStub_unwrap]"); if ((*env)->ExceptionCheck(env)) { + (void) (*ftab->releaseBuffer)(&dummy, &msg); return NULL; } - checkStatus(env, jobj, GSS_ERROR(major), minor, "[GSSLibStub_unwrap]"); + /* + * Map msg to byteArray result and release, zero length msg maps to empty + * byte array, not null. + */ + jresult = getJavaBuffer(env, &msg, JNI_FALSE); if ((*env)->ExceptionCheck(env)) { return NULL; } @@ -1671,15 +1683,18 @@ Java_sun_security_jgss_wrapper_GSSLibStub_unwrap(JNIEnv *env, (*env)->CallVoidMethod(env, jprop, MID_MessageProp_setPrivacy, (confState != 0)); if ((*env)->ExceptionCheck(env)) { + (*env)->DeleteLocalRef(env, jresult); return NULL; } (*env)->CallVoidMethod(env, jprop, MID_MessageProp_setQOP, qop); if ((*env)->ExceptionCheck(env)) { + (*env)->DeleteLocalRef(env, jresult); return NULL; } setSupplementaryInfo(env, jobj, jprop, GSS_SUPPLEMENTARY_INFO(major), minor); if ((*env)->ExceptionCheck(env)) { + (*env)->DeleteLocalRef(env, jresult); return NULL; } diff --git a/src/java.security.jgss/share/native/libj2gss/NativeUtil.c b/src/java.security.jgss/share/native/libj2gss/NativeUtil.c index 3fba9fcdbfa..2d9d78e1572 100644 --- a/src/java.security.jgss/share/native/libj2gss/NativeUtil.c +++ b/src/java.security.jgss/share/native/libj2gss/NativeUtil.c @@ -463,31 +463,34 @@ void throwOutOfMemoryError(JNIEnv *env, const char *message) { jstring getJavaString(JNIEnv *env, gss_buffer_t bytes) { jstring result = NULL; OM_uint32 minor; - int len; + jsize len; jbyteArray jbytes; - if (bytes != NULL) { - /* constructs the String object with new String(byte[]) - NOTE: do NOT include the trailing NULL */ - len = (int) bytes->length; - jbytes = (*env)->NewByteArray(env, len); - if (jbytes == NULL) { - goto finish; - } + if (bytes == NULL) { + return NULL; + } - (*env)->SetByteArrayRegion(env, jbytes, 0, len, (jbyte *) bytes->value); - if ((*env)->ExceptionCheck(env)) { - goto finish; - } + /* constructs the String object with new String(byte[]) */ + len = (jsize)bytes->length; + if (len < 0 || bytes->length != (size_t)len) { + (*ftab->releaseBuffer)(&minor, bytes); + return NULL; + } + jbytes = (*env)->NewByteArray(env, len); + if (jbytes == NULL) { + (*ftab->releaseBuffer)(&minor, bytes); + return NULL; + } + (*env)->SetByteArrayRegion(env, jbytes, 0, len, (jbyte *) bytes->value); + if ((*env)->ExceptionCheck(env) == JNI_FALSE) { result = (*env)->NewObject(env, CLS_String, MID_String_ctor, jbytes); - finish: - (*env)->DeleteLocalRef(env, jbytes); - (*ftab->releaseBuffer)(&minor, bytes); - return result; - } /* else fall through */ - return NULL; + } + + (*env)->DeleteLocalRef(env, jbytes); + (*ftab->releaseBuffer)(&minor, bytes); + return result; } /* * Utility routine for generate message for the specified minor @@ -519,7 +522,7 @@ jstring getMinorMessage(JNIEnv *env, jobject jstub, OM_uint32 statusValue) { * not GSS_S_COMPLETE (i.e. 0). */ void checkStatus(JNIEnv *env, jobject jstub, OM_uint32 major, - OM_uint32 minor, char* methodName) { + OM_uint32 minor, const char *methodName) { int callingErr, routineErr, supplementaryInfo; jint jmajor, jminor; char* msg; @@ -629,27 +632,34 @@ void resetGSSBuffer(gss_buffer_t cbytes) { * NOTE: the specified gss_buffer_t structure is always * released. */ -jbyteArray getJavaBuffer(JNIEnv *env, gss_buffer_t cbytes) { +jbyteArray getJavaBuffer(JNIEnv *env, gss_buffer_t cbytes, jboolean isToken) { jbyteArray result = NULL; - OM_uint32 minor; // don't care, just so it compiles - - if (cbytes != NULL) { - if ((cbytes != GSS_C_NO_BUFFER) && (cbytes->length != 0)) { - result = (*env)->NewByteArray(env, (int) cbytes->length); - if (result == NULL) { - goto finish; - } - (*env)->SetByteArrayRegion(env, result, 0, (int) cbytes->length, + OM_uint32 dummy; + + /* + * Zero length tokens map to NULL outputs, but otherwise to a zero-length + * Java byte array. + */ + if (cbytes != GSS_C_NO_BUFFER && + (isToken == JNI_FALSE || cbytes->length > 0)) { + jsize len = (jsize)cbytes->length; + + if (len < 0 || cbytes->length != (size_t)len) { + /* XXX: Throw exception */ + return NULL; + } + result = (*env)->NewByteArray(env, len); + if (result != NULL) { + (*env)->SetByteArrayRegion(env, result, 0, len, cbytes->value); if ((*env)->ExceptionCheck(env)) { + (*env)->DeleteLocalRef(env, result); result = NULL; } } - finish: - (*ftab->releaseBuffer)(&minor, cbytes); - return result; } - return NULL; + (void) (*ftab->releaseBuffer)(&dummy, cbytes); + return result; } /* @@ -777,13 +787,16 @@ void deleteGSSOIDSet(gss_OID_set oidSet) { * using the specified gss_OID_set structure. */ jobjectArray getJavaOIDArray(JNIEnv *env, gss_OID_set cOidSet) { - int numOfOids = 0; + jsize numOfOids = 0; jobjectArray jOidSet; jobject jOid; - int i; + jsize i; if (cOidSet != NULL && cOidSet != GSS_C_NO_OID_SET) { - numOfOids = (int) cOidSet->count; + numOfOids = (jsize) cOidSet->count; + if (numOfOids < 0 || cOidSet->count != (size_t)numOfOids) { + return NULL; + } jOidSet = (*env)->NewObjectArray(env, numOfOids, CLS_Oid, NULL); if ((*env)->ExceptionCheck(env)) { return NULL; diff --git a/src/java.security.jgss/share/native/libj2gss/NativeUtil.h b/src/java.security.jgss/share/native/libj2gss/NativeUtil.h index 04dee5fbc54..df54812e1a9 100644 --- a/src/java.security.jgss/share/native/libj2gss/NativeUtil.h +++ b/src/java.security.jgss/share/native/libj2gss/NativeUtil.h @@ -36,7 +36,7 @@ extern "C" { #endif extern jint getJavaTime(OM_uint32); extern OM_uint32 getGSSTime(jint); - extern void checkStatus(JNIEnv *, jobject, OM_uint32, OM_uint32, char*); + extern void checkStatus(JNIEnv *, jobject, OM_uint32, OM_uint32, const char *); extern jint checkTime(OM_uint32); extern void throwOutOfMemoryError(JNIEnv *, const char*); extern void initGSSBuffer(JNIEnv *, jbyteArray, gss_buffer_t); @@ -47,7 +47,7 @@ extern "C" { extern gss_OID_set newGSSOIDSet(gss_OID); extern void deleteGSSOIDSet(gss_OID_set); - extern jbyteArray getJavaBuffer(JNIEnv *, gss_buffer_t); + extern jbyteArray getJavaBuffer(JNIEnv *, gss_buffer_t, jboolean); extern jstring getJavaString(JNIEnv *, gss_buffer_t); extern jobject getJavaOID(JNIEnv *, gss_OID); extern jobjectArray getJavaOIDArray(JNIEnv *, gss_OID_set); From 0191c79a51216aeb1f01964d63762cc7b84fcecc Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Fri, 21 Sep 2018 17:02:11 -0500 Subject: [PATCH 04/25] Implement String to gss_buffer_t import --- .../share/native/libj2gss/NativeUtil.c | 94 +++++++++++++------ .../share/native/libj2gss/NativeUtil.h | 2 + 2 files changed, 68 insertions(+), 28 deletions(-) diff --git a/src/java.security.jgss/share/native/libj2gss/NativeUtil.c b/src/java.security.jgss/share/native/libj2gss/NativeUtil.c index 2d9d78e1572..0bb9c5f3214 100644 --- a/src/java.security.jgss/share/native/libj2gss/NativeUtil.c +++ b/src/java.security.jgss/share/native/libj2gss/NativeUtil.c @@ -455,6 +455,14 @@ void throwOutOfMemoryError(JNIEnv *env, const char *message) { throwByName(env, "java/lang/OutOfMemoryError", message); } +static jsize +safe_jsize(size_t n) +{ + jsize res = (jsize)n; + + return (res >= 0 && (size_t)res == n) ? res : -1; +} + /* * Utility routine for creating a java.lang.String object * using the specified gss_buffer_t structure. The specified @@ -471,8 +479,7 @@ jstring getJavaString(JNIEnv *env, gss_buffer_t bytes) { } /* constructs the String object with new String(byte[]) */ - len = (jsize)bytes->length; - if (len < 0 || bytes->length != (size_t)len) { + if ((len = safe_jsize(bytes->length)) < 0) { (*ftab->releaseBuffer)(&minor, bytes); return NULL; } @@ -591,26 +598,25 @@ void initGSSBuffer(JNIEnv *env, jbyteArray jbytes, int len; void* value; - if (jbytes != NULL) { - len = (*env)->GetArrayLength(env, jbytes); - value = malloc(len); - if (value == NULL) { - throwOutOfMemoryError(env, NULL); - return; - } else { - (*env)->GetByteArrayRegion(env, jbytes, 0, len, value); - if ((*env)->ExceptionCheck(env)) { - free(value); - return; - } else { - cbytes->length = len; - cbytes->value = value; - } - } - } else { - cbytes->length = 0; - cbytes->value = NULL; + cbytes->length = 0; + cbytes->value = NULL; + + if (jbytes == NULL) + return; + + len = (*env)->GetArrayLength(env, jbytes); + value = malloc(len); + if (value == NULL) { + throwOutOfMemoryError(env, NULL); + return; } + (*env)->GetByteArrayRegion(env, jbytes, 0, len, value); + if ((*env)->ExceptionCheck(env)) { + free(value); + return; + } + cbytes->length = len; + cbytes->value = value; } /* @@ -626,6 +632,40 @@ void resetGSSBuffer(gss_buffer_t cbytes) { } } +/* + * Utility routine for initializing gss_buffer_t structure + * with a String. + * NOTE: need to call resetGSSBufferString(...) to free up + * the resources. + */ +void initGSSBufferString(JNIEnv* env, jstring jstr, gss_buffer_t buf) +{ + const char *s; + + buf->length = 0; + buf->value = NULL; + if (jstr != NULL) { + s = (*env)->GetStringUTFChars(env, jstr, NULL); + if (s == NULL) { + throwOutOfMemoryError(env, NULL); + } else { + buf->length = strlen(s); + buf->value = (char *)s; /* Drop const */ + } + } +} + +/* + * Utility routine for unpinning/releasing the String + * associated with the specified jstring object. + * NOTE: used in conjunction with initGSSBufferString(...). + */ +void resetGSSBufferString(JNIEnv *env, jstring jstr, gss_buffer_t buf) +{ + if (jstr != NULL && buf->value != NULL) + (*env)->ReleaseStringUTFChars(env, jstr, buf->value); +} + /* * Utility routine for creating a jbyteArray object using * the byte[] value in specified gss_buffer_t structure. @@ -642,13 +682,11 @@ jbyteArray getJavaBuffer(JNIEnv *env, gss_buffer_t cbytes, jboolean isToken) { */ if (cbytes != GSS_C_NO_BUFFER && (isToken == JNI_FALSE || cbytes->length > 0)) { - jsize len = (jsize)cbytes->length; + jsize len = safe_jsize(cbytes->length); - if (len < 0 || cbytes->length != (size_t)len) { - /* XXX: Throw exception */ - return NULL; + if (len >= 0) { + result = (*env)->NewByteArray(env, len); } - result = (*env)->NewByteArray(env, len); if (result != NULL) { (*env)->SetByteArrayRegion(env, result, 0, len, cbytes->value); @@ -793,8 +831,8 @@ jobjectArray getJavaOIDArray(JNIEnv *env, gss_OID_set cOidSet) { jsize i; if (cOidSet != NULL && cOidSet != GSS_C_NO_OID_SET) { - numOfOids = (jsize) cOidSet->count; - if (numOfOids < 0 || cOidSet->count != (size_t)numOfOids) { + numOfOids = safe_jsize(cOidSet->count); + if (numOfOids < 0) { return NULL; } jOidSet = (*env)->NewObjectArray(env, numOfOids, CLS_Oid, NULL); diff --git a/src/java.security.jgss/share/native/libj2gss/NativeUtil.h b/src/java.security.jgss/share/native/libj2gss/NativeUtil.h index df54812e1a9..30d077961e7 100644 --- a/src/java.security.jgss/share/native/libj2gss/NativeUtil.h +++ b/src/java.security.jgss/share/native/libj2gss/NativeUtil.h @@ -41,6 +41,8 @@ extern "C" { extern void throwOutOfMemoryError(JNIEnv *, const char*); extern void initGSSBuffer(JNIEnv *, jbyteArray, gss_buffer_t); extern void resetGSSBuffer(gss_buffer_t); + void initGSSBufferString(JNIEnv *, jstring, gss_buffer_t); + void resetGSSBufferString(JNIEnv *, jstring, gss_buffer_t); extern gss_OID newGSSOID(JNIEnv *, jobject); extern void deleteGSSOID(gss_OID); From f8d26b39217732794f506bfecc562cf910de7165 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Thu, 12 Nov 2015 06:43:43 +0000 Subject: [PATCH 05/25] Revert initGSSBuffer to JDK7 non-copy behaviour --- .../share/native/libj2gss/GSSLibStub.c | 79 ++++++++++--------- .../share/native/libj2gss/NativeUtil.c | 40 +++++++--- .../share/native/libj2gss/NativeUtil.h | 4 +- 3 files changed, 69 insertions(+), 54 deletions(-) diff --git a/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c b/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c index 3f131876ab1..41785f36c5c 100644 --- a/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c +++ b/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c @@ -149,21 +149,20 @@ Java_sun_security_jgss_wrapper_GSSLibStub_getMechPtr(JNIEnv *env, * Utility routine which releases the specified gss_channel_bindings_t * structure. */ -void deleteGSSCB(gss_channel_bindings_t cb) { - +static void deleteGSSCB(JNIEnv *env, gss_channel_bindings_t cb) { if (cb == GSS_C_NO_CHANNEL_BINDINGS) return; /* release initiator address */ if (cb->initiator_addrtype != GSS_C_AF_NULLADDR) { - resetGSSBuffer(&(cb->initiator_address)); + resetGSSBuffer(env, NULL, &(cb->initiator_address)); } /* release acceptor address */ if (cb->acceptor_addrtype != GSS_C_AF_NULLADDR) { - resetGSSBuffer(&(cb->acceptor_address)); + resetGSSBuffer(env, NULL, &(cb->acceptor_address)); } /* release application data */ - if (cb->application_data.length != 0) { - resetGSSBuffer(&(cb->application_data)); + if (cb->application_data.value != NULL) { + resetGSSBuffer(env, NULL, &(cb->application_data)); } free(cb); } @@ -188,9 +187,11 @@ gss_channel_bindings_t newGSSCB(JNIEnv *env, jobject jcb) { return NULL; } - // initialize addrtype in CB first + /* Fully initialize to a state safe for cleanup */ cb->initiator_addrtype = GSS_C_AF_NULLADDR; cb->acceptor_addrtype = GSS_C_AF_NULLADDR; + cb->application_data.length = 0; + cb->application_data.value = NULL; // addresses needs to be initialized to empty memset(&cb->initiator_address, 0, sizeof(cb->initiator_address)); @@ -208,11 +209,11 @@ gss_channel_bindings_t newGSSCB(JNIEnv *env, jobject jcb) { if ((*env)->ExceptionCheck(env)) { goto cleanup; } - cb->initiator_addrtype = GSS_C_AF_INET; - initGSSBuffer(env, value, &(cb->initiator_address)); + initGSSBuffer(env, value, &(cb->initiator_address), JNI_TRUE); if ((*env)->ExceptionCheck(env)) { goto cleanup; } + cb->initiator_addrtype = GSS_C_AF_INET; } /* set up acceptor address */ jinetAddr = (*env)->CallObjectMethod(env, jcb, @@ -226,11 +227,11 @@ gss_channel_bindings_t newGSSCB(JNIEnv *env, jobject jcb) { if ((*env)->ExceptionCheck(env)) { goto cleanup; } - cb->acceptor_addrtype = GSS_C_AF_INET; - initGSSBuffer(env, value, &(cb->acceptor_address)); + initGSSBuffer(env, value, &(cb->acceptor_address), JNI_TRUE); if ((*env)->ExceptionCheck(env)) { goto cleanup; } + cb->acceptor_addrtype = GSS_C_AF_INET; } /* set up application data */ value = (*env)->CallObjectMethod(env, jcb, @@ -238,13 +239,13 @@ gss_channel_bindings_t newGSSCB(JNIEnv *env, jobject jcb) { if ((*env)->ExceptionCheck(env)) { goto cleanup; } - initGSSBuffer(env, value, &(cb->application_data)); + initGSSBuffer(env, value, &(cb->application_data), JNI_TRUE); if ((*env)->ExceptionCheck(env)) { goto cleanup; } return cb; cleanup: - deleteGSSCB(cb); + deleteGSSCB(env, cb); return NULL; } @@ -366,14 +367,14 @@ Java_sun_security_jgss_wrapper_GSSLibStub_importName(JNIEnv *env, TRACE0("[GSSLibStub_importName]"); - initGSSBuffer(env, jnameVal, &nameVal); + initGSSBuffer(env, jnameVal, &nameVal, JNI_FALSE); if ((*env)->ExceptionCheck(env)) { return jlong_zero; } nameType = newGSSOID(env, jnameType); if ((*env)->ExceptionCheck(env)) { - resetGSSBuffer(&nameVal); + resetGSSBuffer(env, jnameVal, &nameVal); return jlong_zero; } @@ -385,7 +386,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_importName(JNIEnv *env, /* release intermediate buffers */ deleteGSSOID(nameType); - resetGSSBuffer(&nameVal); + resetGSSBuffer(env, jnameVal, &nameVal); checkStatus(env, jobj, major, minor, "[GSSLibStub_importName]"); if ((*env)->ExceptionCheck(env)) { @@ -781,7 +782,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_importContext(JNIEnv *env, TRACE0("[GSSLibStub_importContext]"); contextHdl = GSS_C_NO_CONTEXT; - initGSSBuffer(env, jctxtToken, &ctxtToken); + initGSSBuffer(env, jctxtToken, &ctxtToken, JNI_FALSE); if ((*env)->ExceptionCheck(env)) { return NULL; } @@ -793,7 +794,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_importContext(JNIEnv *env, TRACE1("[GSSLibStub_importContext] pContext=%" PRIuPTR "", (uintptr_t) contextHdl); /* release intermediate buffers */ - resetGSSBuffer(&ctxtToken); + resetGSSBuffer(env, jctxtToken, &ctxtToken); checkStatus(env, jobj, major, minor, "[GSSLibStub_importContext]"); /* return immediately if an exception has occurred */ @@ -874,9 +875,9 @@ Java_sun_security_jgss_wrapper_GSSLibStub_initContext(JNIEnv *env, return NULL; } - initGSSBuffer(env, jinToken, &inToken); + initGSSBuffer(env, jinToken, &inToken, JNI_FALSE); if ((*env)->ExceptionCheck(env)) { - deleteGSSCB(cb); + deleteGSSCB(env, cb); return NULL; } @@ -931,8 +932,8 @@ Java_sun_security_jgss_wrapper_GSSLibStub_initContext(JNIEnv *env, } /* release intermediate buffers before checking status */ - deleteGSSCB(cb); - resetGSSBuffer(&inToken); + deleteGSSCB(env, cb); + resetGSSBuffer(env, jinToken, &inToken); checkStatus(env, jobj, major, minor, "[GSSLibStub_initContext]"); if ((*env)->ExceptionCheck(env)) { @@ -980,13 +981,13 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env, contextHdl = contextHdlSave = (gss_ctx_id_t)jlong_to_ptr( (*env)->GetLongField(env, jcontextSpi, FID_NativeGSSContext_pContext)); credHdl = (gss_cred_id_t) jlong_to_ptr(pCred); - initGSSBuffer(env, jinToken, &inToken); + initGSSBuffer(env, jinToken, &inToken, JNI_FALSE); if ((*env)->ExceptionCheck(env)) { return NULL; } cb = newGSSCB(env, jcb); if ((*env)->ExceptionCheck(env)) { - resetGSSBuffer(&inToken); + resetGSSBuffer(env, jinToken, &inToken); return NULL; } setTarget = (credHdl == GSS_C_NO_CREDENTIAL); @@ -1006,8 +1007,8 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env, &aFlags, &aTime, &delCred); /* release intermediate buffers before checking status */ - deleteGSSCB(cb); - resetGSSBuffer(&inToken); + deleteGSSCB(env, cb); + resetGSSBuffer(env, jinToken, &inToken); TRACE3("[GSSLibStub_acceptContext] after: pCred=%" PRIuPTR ", pContext=%" PRIuPTR ", pDelegCred=%" PRIuPTR "", (uintptr_t)credHdl, (uintptr_t)contextHdl, (uintptr_t) delCred); @@ -1444,18 +1445,17 @@ Java_sun_security_jgss_wrapper_GSSLibStub_getMic(JNIEnv *env, jobject jobj, return NULL; } qop = (gss_qop_t) jqop; - initGSSBuffer(env, jmsg, &msg); + initGSSBuffer(env, jmsg, &msg, JNI_FALSE); if ((*env)->ExceptionCheck(env)) { return NULL; } /* gss_get_mic(...) => GSS_S_CONTEXT_EXPIRED, GSS_S_NO_CONTEXT(!), GSS_S_BAD_QOP */ - major = - (*ftab->getMic)(&minor, contextHdl, qop, &msg, &msgToken); + major = (*ftab->getMic)(&minor, contextHdl, qop, &msg, &msgToken); /* release intermediate buffers */ - resetGSSBuffer(&msg); + resetGSSBuffer(env, jmsg, &msg); checkStatus(env, jobj, major, minor, "[GSSLibStub_getMic]"); if ((*env)->ExceptionCheck(env)) { (void) (*ftab->releaseBuffer)(&dummy, &msgToken); @@ -1499,12 +1499,12 @@ Java_sun_security_jgss_wrapper_GSSLibStub_verifyMic(JNIEnv *env, qop = (gss_qop_t) (*env)->CallIntMethod(env, jprop, MID_MessageProp_getQOP); if ((*env)->ExceptionCheck(env)) { return; } - initGSSBuffer(env, jmsg, &msg); + initGSSBuffer(env, jmsg, &msg, JNI_FALSE); if ((*env)->ExceptionCheck(env)) { return; } - initGSSBuffer(env, jmsgToken, &msgToken); + initGSSBuffer(env, jmsgToken, &msgToken, JNI_FALSE); if ((*env)->ExceptionCheck(env)) { - resetGSSBuffer(&msg); + resetGSSBuffer(env, jmsg, &msg); return; } @@ -1515,8 +1515,8 @@ Java_sun_security_jgss_wrapper_GSSLibStub_verifyMic(JNIEnv *env, (*ftab->verifyMic)(&minor, contextHdl, &msg, &msgToken, &qop); /* release intermediate buffers */ - resetGSSBuffer(&msg); - resetGSSBuffer(&msgToken); + resetGSSBuffer(env, jmsg, &msg); + resetGSSBuffer(env, jmsgToken, &msgToken); /* * We don't throw on supplementary status codes here, instead we pass only @@ -1580,7 +1580,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_wrap(JNIEnv *env, return NULL; } - initGSSBuffer(env, jmsg, &msg); + initGSSBuffer(env, jmsg, &msg, JNI_FALSE); if ((*env)->ExceptionCheck(env)) { return NULL; } @@ -1591,8 +1591,9 @@ Java_sun_security_jgss_wrapper_GSSLibStub_wrap(JNIEnv *env, &msgToken); /* release intermediate buffers */ - resetGSSBuffer(&msg); + resetGSSBuffer(env, jmsg, &msg); checkStatus(env, jobj, major, minor, "[GSSLibStub_wrap]"); + if ((*env)->ExceptionCheck(env)) { (void) (*ftab->releaseBuffer)(&dummy, &msgToken); return NULL; @@ -1643,7 +1644,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_unwrap(JNIEnv *env, return NULL; } - initGSSBuffer(env, jmsgToken, &msgToken); + initGSSBuffer(env, jmsgToken, &msgToken, JNI_FALSE); if ((*env)->ExceptionCheck(env)) { return NULL; } @@ -1657,7 +1658,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_unwrap(JNIEnv *env, (*ftab->unwrap)(&minor, contextHdl, &msgToken, &msg, &confState, &qop); /* release intermediate buffers */ - resetGSSBuffer(&msgToken); + resetGSSBuffer(env, jmsgToken, &msgToken); /* * We don't throw on supplementary status codes here, instead we pass only diff --git a/src/java.security.jgss/share/native/libj2gss/NativeUtil.c b/src/java.security.jgss/share/native/libj2gss/NativeUtil.c index 0bb9c5f3214..b2a8889e0fa 100644 --- a/src/java.security.jgss/share/native/libj2gss/NativeUtil.c +++ b/src/java.security.jgss/share/native/libj2gss/NativeUtil.c @@ -590,45 +590,59 @@ void checkStatus(JNIEnv *env, jobject jstub, OM_uint32 major, * Utility routine for initializing gss_buffer_t structure * with the byte[] in the specified jbyteArray object. * NOTE: must call resetGSSBuffer() to free up the resources - * inside the gss_buffer_t structure. */ void initGSSBuffer(JNIEnv *env, jbyteArray jbytes, - gss_buffer_t cbytes) { - - int len; + gss_buffer_t cbytes, jboolean wantCopy) +{ + jboolean isCopy; + jint len; void* value; cbytes->length = 0; cbytes->value = NULL; - if (jbytes == NULL) + if (jbytes == NULL || + (len = (*env)->GetArrayLength(env, jbytes)) == 0) return; - len = (*env)->GetArrayLength(env, jbytes); + cbytes->length = len; + + if (wantCopy == JNI_FALSE) { + cbytes->value = (*env)->GetByteArrayElements(env, jbytes, &isCopy); + if (cbytes->value == NULL) { + throwOutOfMemoryError(env, NULL); + } + return; + } + value = malloc(len); if (value == NULL) { throwOutOfMemoryError(env, NULL); return; } + (*env)->GetByteArrayRegion(env, jbytes, 0, len, value); if ((*env)->ExceptionCheck(env)) { free(value); return; } - cbytes->length = len; cbytes->value = value; } /* - * Utility routine for freeing the bytes malloc'ed - * in initGSSBuffer() method. - * NOTE: used in conjunction with initGSSBuffer(...). + * Utility routine for freeing the buffer obtained via initGSSBuffer(). + * If jbytes is null this is a malloced copy. */ -void resetGSSBuffer(gss_buffer_t cbytes) { - if ((cbytes != NULL) && (cbytes != GSS_C_NO_BUFFER)) { +void resetGSSBuffer(JNIEnv *env, jbyteArray jbytes, gss_buffer_t cbytes) +{ + if (cbytes->value == NULL) + return; + if (jbytes != NULL) { + (*env)->ReleaseByteArrayElements(env, jbytes, cbytes->value, JNI_ABORT); + } else if (cbytes->length > 0) { free(cbytes->value); - cbytes->length = 0; cbytes->value = NULL; + cbytes->length = 0; } } diff --git a/src/java.security.jgss/share/native/libj2gss/NativeUtil.h b/src/java.security.jgss/share/native/libj2gss/NativeUtil.h index 30d077961e7..9f30519ce90 100644 --- a/src/java.security.jgss/share/native/libj2gss/NativeUtil.h +++ b/src/java.security.jgss/share/native/libj2gss/NativeUtil.h @@ -39,8 +39,8 @@ extern "C" { extern void checkStatus(JNIEnv *, jobject, OM_uint32, OM_uint32, const char *); extern jint checkTime(OM_uint32); extern void throwOutOfMemoryError(JNIEnv *, const char*); - extern void initGSSBuffer(JNIEnv *, jbyteArray, gss_buffer_t); - extern void resetGSSBuffer(gss_buffer_t); + extern void initGSSBuffer(JNIEnv *, jbyteArray, gss_buffer_t, jboolean); + void resetGSSBuffer(JNIEnv *, jbyteArray, gss_buffer_t); void initGSSBufferString(JNIEnv *, jstring, gss_buffer_t); void resetGSSBufferString(JNIEnv *, jstring, gss_buffer_t); From 2a9e74405c6a960ff2a418912cf9becef958d187 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Wed, 11 Nov 2015 04:12:07 +0000 Subject: [PATCH 06/25] Fix loss of GSS_S_FAILURE major status in importContext --- src/java.security.jgss/share/native/libj2gss/GSSLibStub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c b/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c index 41785f36c5c..2dd1dc9273d 100644 --- a/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c +++ b/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c @@ -824,7 +824,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_importContext(JNIEnv *env, } else { /* mech mismatch - clean up then return null */ major = (*ftab->deleteSecContext)(&minor, &contextHdl, GSS_C_NO_BUFFER); - checkStatus(env, jobj, major, minor, + checkStatus(env, jobj, GSS_S_FAILURE, minor, "[GSSLibStub_importContext] cleanup"); return NULL; } From e2ad2c5118b067a0988fa154a9732b2d14c1b569 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Wed, 9 Dec 2015 23:56:26 +0000 Subject: [PATCH 07/25] Add actual mechanism to native GSSNameElement state --- .../security/jgss/wrapper/GSSCredElement.java | 2 +- .../security/jgss/wrapper/GSSNameElement.java | 10 +++--- .../jgss/wrapper/NativeGSSContext.java | 9 +++--- .../share/native/libj2gss/GSSLibStub.c | 31 ++++++++++--------- .../share/native/libj2gss/NativeUtil.c | 4 +-- 5 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java index 019fa6f8052..6c9711e80b0 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java @@ -80,7 +80,7 @@ void doServicePermCheck() throws GSSException { pCred = cStub.acquireCred(this.name.pName, lifetime, usage); } else { pCred = cStub.acquireCred(0, lifetime, usage); - this.name = new GSSNameElement(cStub.getCredName(pCred), cStub); + this.name = new GSSNameElement(cStub.getCredName(pCred), cStub.getMech(), cStub); doServicePermCheck(); } } diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java index 88274c1601b..53c62b2a4b9 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java @@ -53,6 +53,7 @@ public class GSSNameElement implements GSSNameSpi { long pName = 0; // Pointer to the gss_name_t structure private String printableName; private Oid printableType; + private Oid mech; private GSSLibStub cStub; static final GSSNameElement DEF_ACCEPTOR = new GSSNameElement(); @@ -97,13 +98,14 @@ private GSSNameElement() { printableName = ""; } - GSSNameElement(long pNativeName, GSSLibStub stub) throws GSSException { + GSSNameElement(long pNativeName, Oid mech, 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; + this.mech = mech; cStub = stub; setPrintables(); } @@ -116,6 +118,7 @@ private GSSNameElement() { } cStub = stub; byte[] name = nameBytes; + mech = cStub.getMech(); if (nameType != null) { // Special handling the specified name type if @@ -128,7 +131,6 @@ private GSSNameElement() { // 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) { @@ -195,7 +197,7 @@ private void setPrintables() throws GSSException { public String getKrbName() throws GSSException { long mName = 0; GSSLibStub stub = cStub; - if (!GSSUtil.isKerberosMech(cStub.getMech())) { + if (!GSSUtil.isKerberosMech(mech)) { stub = GSSLibStub.getInstance(GSSUtil.GSS_KRB5_MECH_OID); } mName = stub.canonicalizeName(pName); @@ -267,7 +269,7 @@ public byte[] export() throws GSSException { } public Oid getMechanism() { - return cStub.getMech(); + return (mech != null) ? mech : cStub.getMech(); } public String toString() { diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java index d2d5367f1b8..1d16256e304 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java @@ -236,8 +236,8 @@ private byte[] retrieveToken(InputStream is, int mechTokenLen) 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); + srcName = new GSSNameElement(info[0], actualMech, cStub); + targetName = new GSSNameElement(info[1], actualMech, cStub); isInitiator = (info[2] != 0); isEstablished = (info[3] != 0); flags = (int) info[4]; @@ -294,7 +294,8 @@ public byte[] initSecContext(InputStream is, int mechTokenLen) if (isEstablished) { if (srcName == null) { srcName = new GSSNameElement - (cStub.getContextName(pContext, true), cStub); + (cStub.getContextName(pContext, true), actualMech, + cStub); } if (cred == null) { cred = new GSSCredElement(srcName, lifetime, @@ -320,7 +321,7 @@ public byte[] acceptSecContext(InputStream is, int mechTokenLen) if (targetName == null) { targetName = new GSSNameElement - (cStub.getContextName(pContext, false), cStub); + (cStub.getContextName(pContext, false), actualMech, cStub); // Replace the current default acceptor cred now that // the context acceptor name is available if (cred != null) cred.dispose(); diff --git a/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c b/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c index 2dd1dc9273d..04fa1e6831a 100644 --- a/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c +++ b/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c @@ -854,10 +854,8 @@ Java_sun_security_jgss_wrapper_GSSLibStub_initContext(JNIEnv *env, gss_channel_bindings_t cb; gss_buffer_desc inToken; gss_buffer_desc outToken = GSS_C_EMPTY_BUFFER; -/* UNCOMMENT after SEAM bug#6287358 is backported to S10 gss_OID aMech; jobject jMech; -*/ TRACE0("[GSSLibStub_initContext]"); @@ -891,7 +889,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_initContext(JNIEnv *env, GSS_S_BAD_NAMETYPE, GSS_S_BAD_NAME(!), GSS_S_BAD_MECH */ major = (*ftab->initSecContext)(&minor, credHdl, &contextHdl, targetName, mech, - flags, time, cb, &inToken, NULL /*aMech*/, + flags, time, cb, &inToken, &aMech, &outToken, &aFlags, &aTime); TRACE2("[GSSLibStub_initContext] after: pContext=%" PRIuPTR ", outToken len=%ld", @@ -920,11 +918,9 @@ Java_sun_security_jgss_wrapper_GSSLibStub_initContext(JNIEnv *env, FID_NativeGSSContext_isEstablished, JNI_TRUE); -/* UNCOMMENT after SEAM bug#6287358 is backported to S10 jMech = getJavaOID(env, aMech); (*env)->SetObjectField(env, jcontextSpi, FID_NativeGSSContext_actualMech, jMech); -*/ } else if (major & GSS_S_CONTINUE_NEEDED) { TRACE0("[GSSLibStub_initContext] context not established"); major &= ~GSS_S_CONTINUE_NEEDED; @@ -1024,7 +1020,20 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env, if (GSS_ERROR(major) == GSS_S_COMPLETE) { /* update member values if needed */ - // WORKAROUND for an old Heimdal bug + + if (aMech != GSS_C_NO_OID) { + jMech = getJavaOID(env, aMech); + if ((*env)->ExceptionCheck(env)) { + goto error; + } + (*env)->SetObjectField(env, jcontextSpi, + FID_NativeGSSContext_actualMech, jMech); + if ((*env)->ExceptionCheck(env)) { + goto error; + } + } + + /* WORKAROUND for an old Heimdal bug */ if (delCred == GSS_C_NO_CREDENTIAL) { aFlags &= ~GSS_C_DELEG_FLAG; } @@ -1043,7 +1052,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env, jtargetName = (*env)->NewObject(env, CLS_GSSNameElement, MID_GSSNameElement_ctor, - ptr_to_jlong(targetName), jobj); + ptr_to_jlong(targetName), jMech, jobj); if ((*env)->ExceptionCheck(env)) { goto error; } @@ -1061,7 +1070,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env, if (srcName != GSS_C_NO_NAME) { jsrcName = (*env)->NewObject(env, CLS_GSSNameElement, MID_GSSNameElement_ctor, - ptr_to_jlong(srcName), jobj); + ptr_to_jlong(srcName), jMech, jobj); if ((*env)->ExceptionCheck(env)) { goto error; } @@ -1083,12 +1092,6 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env, (*env)->SetBooleanField(env, jcontextSpi, FID_NativeGSSContext_isEstablished, JNI_TRUE); - jMech = getJavaOID(env, aMech); - if ((*env)->ExceptionCheck(env)) { - goto error; - } - (*env)->SetObjectField(env, jcontextSpi, - FID_NativeGSSContext_actualMech, jMech); if ((*env)->ExceptionCheck(env)) { goto error; } diff --git a/src/java.security.jgss/share/native/libj2gss/NativeUtil.c b/src/java.security.jgss/share/native/libj2gss/NativeUtil.c index b2a8889e0fa..805420575a6 100644 --- a/src/java.security.jgss/share/native/libj2gss/NativeUtil.c +++ b/src/java.security.jgss/share/native/libj2gss/NativeUtil.c @@ -271,9 +271,9 @@ DEF_JNI_OnLoad(JavaVM *jvm, void *reserved) { } MID_GSSNameElement_ctor = (*env)->GetMethodID(env, CLS_GSSNameElement, - "", "(JLsun/security/jgss/wrapper/GSSLibStub;)V"); + "", "(JLorg/ietf/jgss/Oid;Lsun/security/jgss/wrapper/GSSLibStub;)V"); if (MID_GSSNameElement_ctor == NULL) { - printf("Couldn't find GSSNameElement(long, GSSLibStub) constructor\n"); + printf("Couldn't find GSSNameElement(long, Oid, GSSLibStub) constructor\n"); return JNI_ERR; } MID_GSSCredElement_ctor = From 63dcbe5c7e9da5d07c2a27a41de6793c200fc572 Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Tue, 1 Mar 2016 03:52:00 +0000 Subject: [PATCH 08/25] Add getLocalName() GSSName method --- .../share/classes/org/ietf/jgss/GSSName.java | 10 +++ .../sun/security/jgss/GSSNameImpl.java | 37 ++++++++++- .../security/jgss/krb5/Krb5NameElement.java | 26 ++++++++ .../sun/security/jgss/spi/GSSNameSpi.java | 17 +++++ .../sun/security/jgss/wrapper/GSSLibStub.java | 1 + .../security/jgss/wrapper/GSSNameElement.java | 10 +++ .../share/native/libj2gss/GSSLibStub.c | 63 +++++++++++++++++++ .../share/native/libj2gss/NativeFunc.c | 7 +++ .../share/native/libj2gss/NativeFunc.h | 7 +++ .../share/native/libj2gss/gssapi.h | 9 +++ 10 files changed, 186 insertions(+), 1 deletion(-) diff --git a/src/java.security.jgss/share/classes/org/ietf/jgss/GSSName.java b/src/java.security.jgss/share/classes/org/ietf/jgss/GSSName.java index d55bd6abb47..f0bd258b55f 100644 --- a/src/java.security.jgss/share/classes/org/ietf/jgss/GSSName.java +++ b/src/java.security.jgss/share/classes/org/ietf/jgss/GSSName.java @@ -261,6 +261,16 @@ public interface GSSName { */ public byte[] export() throws GSSException; + /** + * Returns a local username form of a mechanism name, if available. + */ + public String getLocalName() throws GSSException; + + /** + * Returns a local username form of a mechanism name, if available. + */ + public String getLocalName(Oid mech) throws GSSException; + /** * Returns a textual representation of the GSSName object. To retrieve * the printed name format, which determines the syntax of the returned diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/GSSNameImpl.java b/src/java.security.jgss/share/classes/sun/security/jgss/GSSNameImpl.java index e1e89059c5f..97badb1e71c 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/GSSNameImpl.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/GSSNameImpl.java @@ -447,8 +447,43 @@ public byte[] export() throws GSSException { } public String toString() { - return printableName; + return printableName; + } + public String getLocalName() throws GSSException { + String lname = null; + Oid mech = null; + Oid mech2 = null; + + for (GSSNameSpi v : elements.values()) { + String mname = v.getLocalName(); + if (mname == null) + continue; + if (lname == null) { + mech = v.getMechanism(); + lname = mname; + continue; + } + if (!lname.equals(mname)) { + mech2 = v.getMechanism(); + break; + } + } + if (mech2 == null) + return lname; + throw new GSSExceptionImpl(GSSException.UNAVAILABLE, + "Localname conflict between mechanisms " + + mech + " and " + mech2); + } + + public String getLocalName(Oid mech) throws GSSException { + GSSNameSpi element = elements.get(mech); + if (element == null) { + throw new GSSExceptionImpl(GSSException.UNAVAILABLE, + "GSSName object does not have an element for the " + + "given mechanism"); + } + return element.getLocalName(); } public Oid getStringNameType() throws GSSException { diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5NameElement.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5NameElement.java index 1d0217e9187..4c207bfc8d5 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5NameElement.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5NameElement.java @@ -316,6 +316,32 @@ public String toString() { // For testing: return (super.toString()); } + /** + * Returns a local username (platform-specific) corresponding to the + * principal name, and may return null when no username is known for the + * principal name. + * + * @return username corresponding to this principal name, if any + */ + public String getLocalName() throws GSSException { + throw new GSSException(GSSException.UNAVAILABLE, -1, + "Mapping Kerberos principal names to usernames is not " + + "currently supported"); + } + + /** + * Returns a local username (platform-specific) corresponding to the + * principal name, and may return null when no username is known for the + * principal name. + * + * @return username corresponding to this principal name, if any + */ + public String getLocalName(Oid mech) throws GSSException { + throw new GSSException(GSSException.UNAVAILABLE, -1, + "Mapping Kerberos principal names to usernames is not " + + "currently supported"); + } + /** * Returns the name type oid. */ diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSNameSpi.java b/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSNameSpi.java index 24362d0074f..bfb93f93719 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSNameSpi.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSNameSpi.java @@ -100,6 +100,23 @@ public interface GSSNameSpi { */ public String toString(); + /** + * Returns a local username (platform-specific) corresponding to the + * principal name, and may return null when no username is known for the + * principal name. + * + * @return username corresponding to this name, if any + */ + public String getLocalName() throws GSSException; + + /** + * Returns a local username (platform-specific) corresponding to the + * principal name, and may return null when no username is known for the + * principal name. + * + * @return username corresponding to this name, if any + */ + public String getLocalName(Oid mech) throws GSSException; /** * Returns the oid describing the format of the printable name. diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSLibStub.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSLibStub.java index 900532459bf..cbdf7a3b591 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSLibStub.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSLibStub.java @@ -65,6 +65,7 @@ class GSSLibStub { native boolean compareName(long pName1, long pName2); native long canonicalizeName(long pName); native byte[] exportName(long pName) throws GSSException; + native String localName(long pName, Oid mech) throws GSSException; native Object[] displayName(long pName) throws GSSException; // Credential related routines diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java index 53c62b2a4b9..9952ee12d17 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java @@ -276,6 +276,16 @@ public String toString() { return printableName; } + public String getLocalName() throws GSSException { + return cStub.localName(pName, mech); + } + + public String getLocalName(Oid mech) throws GSSException { + if (mech.equals(this.mech)) + return cStub.localName(pName, mech); + throw new GSSException(GSSException.BAD_MECH); + } + public Oid getStringNameType() { return printableType; } diff --git a/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c b/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c index 04fa1e6831a..cd8547a89ce 100644 --- a/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c +++ b/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c @@ -512,6 +512,69 @@ Java_sun_security_jgss_wrapper_GSSLibStub_exportName(JNIEnv *env, return getJavaBuffer(env, &outBuf, JNI_TRUE); } +/* + * Class: sun_security_jgss_wrapper_GSSLibStub + * Method: localName + * Signature: (J)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL +Java_sun_security_jgss_wrapper_GSSLibStub_localName(JNIEnv *env, + jobject jobj, + jlong pName, + jobject jOid) +{ + OM_uint32 minor, major, dummy; + gss_name_t nameHdl, mnNameHdl; + gss_buffer_desc outBuf = GSS_C_EMPTY_BUFFER; + gss_OID mech; + + nameHdl = (gss_name_t) jlong_to_ptr(pName); + + if (ftab->localName == NULL) { + TRACE0("GSSLibStub_localName not supported by GSS provider"); + checkStatus(env, jobj, GSS_S_UNAVAILABLE, minor=0, + "[GSSLibStub_localName]"); + return NULL; + } + mech = newGSSOID(env, jOid); + + /* gss_localname(...) => GSS_S_NAME_NOT_MN, GSS_S_BAD_NAMETYPE, + GSS_S_BAD_NAME */ + major = (*ftab->localName)(&minor, nameHdl, mech, &outBuf); + if (major == GSS_S_COMPLETE) { + deleteGSSOID(mech); + return getJavaString(env, &outBuf); + } + (*ftab->releaseBuffer)(&minor, &outBuf); + + if (major != GSS_S_NAME_NOT_MN) { + checkStatus(env, jobj, major, minor, "[GSSLibStub_localName]"); + goto err; + } + + /* canonicalize the internal name to MN and retry */ + TRACE0("[GSSLibStub_localName] canonicalize and re-try"); + + major = (*ftab->canonicalizeName)(&minor, nameHdl, mech, &mnNameHdl); + checkStatus(env, jobj, major, minor, "[GSSLibStub_localName]"); + if ((*env)->ExceptionCheck(env)) + goto err; + + major = (*ftab->localName)(&minor, mnNameHdl, mech, &outBuf); + (void) (*ftab->releaseName)(&dummy, &mnNameHdl); + + checkStatus(env, jobj, major, minor, "[GSSLibStub_localName]"); + if ((*env)->ExceptionCheck(env) == JNI_FALSE && major == GSS_S_COMPLETE) { + deleteGSSOID(mech); + return getJavaString(env, &outBuf); + } + (*ftab->releaseBuffer)(&minor, &outBuf); + +err: + deleteGSSOID(mech); + return NULL; +} + /* * Class: sun_security_jgss_wrapper_GSSLibStub * Method: displayName diff --git a/src/java.security.jgss/share/native/libj2gss/NativeFunc.c b/src/java.security.jgss/share/native/libj2gss/NativeFunc.c index da7bc0445b9..69b3b9cdfe7 100644 --- a/src/java.security.jgss/share/native/libj2gss/NativeFunc.c +++ b/src/java.security.jgss/share/native/libj2gss/NativeFunc.c @@ -34,6 +34,7 @@ static const char COMPARE_NAME[] = "gss_compare_name"; static const char CANONICALIZE_NAME[] = "gss_canonicalize_name"; static const char EXPORT_NAME[] = "gss_export_name"; static const char DISPLAY_NAME[] = "gss_display_name"; +static const char LOCAL_NAME[] = "gss_localname"; static const char ACQUIRE_CRED[] = "gss_acquire_cred"; static const char RELEASE_CRED[] = "gss_release_cred"; static const char INQUIRE_CRED[] = "gss_inquire_cred"; @@ -122,6 +123,12 @@ int loadNative(const char *libName) { goto out; } + /* + * This one may not be available for a given GSS library, as it's an + * extension, therefore we don't fail if it's missing. + */ + ftab->localName = (LOCAL_NAME_FN_PTR)GETFUNC(gssLib, LOCAL_NAME); + ftab->acquireCred = (ACQUIRE_CRED_FN_PTR)GETFUNC(gssLib, ACQUIRE_CRED); if (ftab->acquireCred == NULL) { failed = TRUE; diff --git a/src/java.security.jgss/share/native/libj2gss/NativeFunc.h b/src/java.security.jgss/share/native/libj2gss/NativeFunc.h index bf4ca99c176..7aeaaea4f8a 100644 --- a/src/java.security.jgss/share/native/libj2gss/NativeFunc.h +++ b/src/java.security.jgss/share/native/libj2gss/NativeFunc.h @@ -84,6 +84,12 @@ typedef OM_uint32 (*DISPLAY_NAME_FN_PTR) gss_buffer_t output_name_buffer, gss_OID *output_name_type); +typedef OM_uint32 (*LOCAL_NAME_FN_PTR) + (OM_uint32 *minor_status, + gss_name_t input_name, + gss_OID mech, + gss_buffer_t output_name_buffer); + typedef OM_uint32 (*ACQUIRE_CRED_FN_PTR) (OM_uint32 *minor_status, gss_name_t desired_name, @@ -249,6 +255,7 @@ typedef struct GSS_FUNCTION_TABLE { CANONICALIZE_NAME_FN_PTR canonicalizeName; EXPORT_NAME_FN_PTR exportName; DISPLAY_NAME_FN_PTR displayName; + LOCAL_NAME_FN_PTR localName; ACQUIRE_CRED_FN_PTR acquireCred; RELEASE_CRED_FN_PTR releaseCred; INQUIRE_CRED_FN_PTR inquireCred; diff --git a/src/java.security.jgss/share/native/libj2gss/gssapi.h b/src/java.security.jgss/share/native/libj2gss/gssapi.h index a1f1e3456c1..7acdead0320 100644 --- a/src/java.security.jgss/share/native/libj2gss/gssapi.h +++ b/src/java.security.jgss/share/native/libj2gss/gssapi.h @@ -686,6 +686,15 @@ GSS_DLLIMP OM_uint32 gss_canonicalize_name( # pragma pack(pop) #endif +/* Common extension for aname2lname (NOT in RFC2744) */ +OM_uint32 +gss_localname( + OM_uint32 *, /* minor_status */ + const gss_name_t, /* name */ + gss_OID, /* mech_type */ + gss_buffer_t /* localname */ +); + #ifdef __cplusplus } #endif From a742c8e61aaa1ed6275ec771cee977007e3d4d1c Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Wed, 11 Nov 2015 04:58:38 +0000 Subject: [PATCH 09/25] Add createCredential() with password Also avoid memory allocation in newGSSOIDSet() renamed to makeGSSOIDset() which now takes a singleton set argument and either assigns the requested OID or with SPNEGO returns a static list of all the supported mechs. With this we no longer need deleteGSSOIDSet(). --- .../classes/org/ietf/jgss/GSSManager.java | 110 ++++++++++++++++++ .../sun/security/jgss/GSSCredentialImpl.java | 49 ++++++-- .../sun/security/jgss/GSSManagerImpl.java | 25 ++++ .../security/jgss/krb5/Krb5MechFactory.java | 18 ++- .../security/jgss/spi/MechanismFactory.java | 48 ++++++++ .../jgss/spnego/SpNegoMechFactory.java | 16 +++ .../security/jgss/wrapper/GSSCredElement.java | 14 ++- .../sun/security/jgss/wrapper/GSSLibStub.java | 4 +- .../jgss/wrapper/NativeGSSFactory.java | 30 ++++- .../share/native/libj2gss/GSSLibStub.c | 37 ++++-- .../share/native/libj2gss/NativeFunc.c | 8 ++ .../share/native/libj2gss/NativeFunc.h | 12 ++ .../share/native/libj2gss/NativeUtil.c | 41 ++----- .../share/native/libj2gss/NativeUtil.h | 3 +- .../share/native/libj2gss/gssapi.h | 17 +++ 15 files changed, 371 insertions(+), 61 deletions(-) diff --git a/src/java.security.jgss/share/classes/org/ietf/jgss/GSSManager.java b/src/java.security.jgss/share/classes/org/ietf/jgss/GSSManager.java index f9ae3efafee..ab3181302c2 100644 --- a/src/java.security.jgss/share/classes/org/ietf/jgss/GSSManager.java +++ b/src/java.security.jgss/share/classes/org/ietf/jgss/GSSManager.java @@ -423,6 +423,58 @@ public abstract GSSCredential createCredential (GSSName name, int lifetime, Oid mech, int usage) throws GSSException; + /** + * Factory method for acquiring a single mechanism credential with a + * password.

+ * + * 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.

+ * + * 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 null 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 (Oid) null + * 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, + String password, 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 @@ -480,6 +532,64 @@ public abstract GSSCredential createCredential(GSSName name, int lifetime, Oid mechs[], int usage) throws GSSException; + /** + * Factory method for acquiring credentials with a password 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.

+ * + * 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.

+ * + * 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 null 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 (Oid[]) null 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, + String password, int lifetime, + Oid mechs[], int usage) + throws GSSException; + /** * Factory method for creating a context on the initiator's * side. diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/GSSCredentialImpl.java b/src/java.security.jgss/share/classes/sun/security/jgss/GSSCredentialImpl.java index 6d0d711325e..18eccb14b18 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/GSSCredentialImpl.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/GSSCredentialImpl.java @@ -66,17 +66,33 @@ protected GSSCredentialImpl(GSSCredentialImpl src) { } GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name, - int lifetime, Oid mech, int usage) + int lifetime, Oid mech, int usage) throws GSSException { if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; init(gssManager); - add(name, lifetime, lifetime, mech, usage); + String password = null; + add(name, password, lifetime, lifetime, mech, usage); + } + + GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name, String password, + int lifetime, Oid mech, int usage) + throws GSSException { + if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; + + init(gssManager); + add(name, password, lifetime, lifetime, mech, usage); } GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name, int lifetime, Oid[] mechs, int usage) throws GSSException { + this(gssManager, name, (String)null, lifetime, mechs, usage); + } + + GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name, + String password, int lifetime, Oid mechs[], int usage) + throws GSSException { init(gssManager); boolean defaultList = false; if (mechs == null) { @@ -86,7 +102,7 @@ protected GSSCredentialImpl(GSSCredentialImpl src) { for (int i = 0; i < mechs.length; i++) { try { - add(name, lifetime, lifetime, mechs[i], usage); + add(name, password, lifetime, lifetime, mechs[i], usage); } catch (GSSException e) { if (defaultList) { // Try the next mechanism @@ -417,6 +433,13 @@ public Oid[] getMechs() throws GSSException { public void add(GSSName name, int initLifetime, int acceptLifetime, Oid mech, int usage) throws GSSException { + String password = null; + add(name, password, initLifetime, acceptLifetime, mech, usage); + } + + public void add(GSSName name, String password, int initLifetime, + int acceptLifetime, Oid mech, int usage) + throws GSSException { if (destroyed) { throw new IllegalStateException("This credential is " + @@ -437,6 +460,7 @@ public void add(GSSName name, int initLifetime, int acceptLifetime, ((GSSNameImpl)name).getElement(mech)); tempCred = gssManager.getCredentialElement(nameElement, + password, initLifetime, acceptLifetime, mech, @@ -474,11 +498,20 @@ public void add(GSSName name, int initLifetime, int acceptLifetime, key = new SearchKey(mech, currentUsage); hashtable.put(key, tempCred); - tempCred = gssManager.getCredentialElement(nameElement, - initLifetime, - acceptLifetime, - mech, - desiredUsage); + if (password == null) { + tempCred = gssManager.getCredentialElement(nameElement, + initLifetime, + acceptLifetime, + mech, + desiredUsage); + } else { + tempCred = gssManager.getCredentialElement(nameElement, + password, + initLifetime, + acceptLifetime, + mech, + desiredUsage); + } key = new SearchKey(mech, desiredUsage); hashtable.put(key, tempCred); diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/GSSManagerImpl.java b/src/java.security.jgss/share/classes/sun/security/jgss/GSSManagerImpl.java index e804a831e5a..8298be9926b 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/GSSManagerImpl.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/GSSManagerImpl.java @@ -135,12 +135,26 @@ public GSSCredential createCredential(GSSName aName, return wrap(new GSSCredentialImpl(this, aName, lifetime, mech, usage)); } + public GSSCredential createCredential(GSSName aName, String password, + int lifetime, Oid mech, int usage) + throws GSSException { + return new GSSCredentialImpl(this, aName, password, + 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 GSSCredential createCredential(GSSName aName, String password, + int lifetime, Oid mechs[], int usage) + throws GSSException { + return new GSSCredentialImpl(this, aName, password, + lifetime, mechs, usage); + } + public GSSContext createContext(GSSName peer, Oid mech, GSSCredential myCred, int lifetime) throws GSSException { @@ -175,6 +189,17 @@ public GSSCredentialSpi getCredentialElement(GSSNameSpi name, int initLifetime, acceptLifetime, usage); } + public GSSCredentialSpi getCredentialElement(GSSNameSpi name, + String password, + int initLifetime, + int acceptLifetime, + Oid mech, int usage) + throws GSSException { + MechanismFactory factory = list.getMechFactory(mech); + return factory.getCredentialElement(name, password, initLifetime, + acceptLifetime, usage); + } + // Used by java SPNEGO impl public GSSNameSpi getNameElement(String name, Oid nameType, Oid mech) throws GSSException { diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java index cc9626c14f7..7a857865eca 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java @@ -108,9 +108,17 @@ public GSSNameSpi getNameElement(byte[] name, Oid nameType) } public GSSCredentialSpi getCredentialElement(GSSNameSpi name, - int initLifetime, int acceptLifetime, + String password, int initLifetime, int acceptLifetime, int usage) throws GSSException { + if (password != null) { + // XXX Implement! Shouldn't be too hard... + throw new GSSException(GSSException.UNAVAILABLE, -1, + "The Kerberos mechanism Java implementation does not " + + "currently support acquiring GSS credentials handle " + + "elements with a password"); + } + if (name != null && !(name instanceof Krb5NameElement)) { name = Krb5NameElement.getInstance(name.toString(), name.getStringNameType()); @@ -139,6 +147,12 @@ public GSSCredentialSpi getCredentialElement(GSSNameSpi name, return credElement; } + public GSSCredentialSpi getCredentialElement(GSSNameSpi name, + int initLifetime, int acceptLifetime, int usage) + throws GSSException { + return getCredentialElement(name, null, initLifetime, acceptLifetime, usage); + } + public static void checkInitCredPermission(Krb5NameElement name) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -151,7 +165,7 @@ public static void checkInitCredPermission(Krb5NameElement name) { sm.checkPermission(perm); } catch (SecurityException e) { if (DEBUG) { - System.out.println("Permission to initiate" + + System.out.println("Permission to initiate " + "kerberos init credential" + e.getMessage()); } throw e; diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/spi/MechanismFactory.java b/src/java.security.jgss/share/classes/sun/security/jgss/spi/MechanismFactory.java index e5bba5f2931..51ce06614a8 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/spi/MechanismFactory.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/spi/MechanismFactory.java @@ -118,6 +118,54 @@ public interface MechanismFactory { public GSSCredentialSpi getCredentialElement(GSSNameSpi name, int initLifetime, int acceptLifetime, int usage) throws GSSException; + /** + * Creates a credential element using a password 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.

+ * + * 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. + * + * @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, + String password, 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 diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoMechFactory.java b/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoMechFactory.java index 511547b8a78..5ae19bed343 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoMechFactory.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoMechFactory.java @@ -149,6 +149,22 @@ public GSSCredentialSpi getCredentialElement(GSSNameSpi name, return credElement; } + public GSSCredentialSpi getCredentialElement(GSSNameSpi name, + String password, 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, password, initLifetime, + acceptLifetime, null, usage)); + } + return credElement; + } + public GSSContextSpi getMechanismContext(GSSNameSpi peer, GSSCredentialSpi myInitiatorCred, int lifetime) throws GSSException { diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java index 6c9711e80b0..d457baf6e57 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java @@ -69,22 +69,28 @@ void doServicePermCheck() throws GSSException { name = srcName; } - GSSCredElement(GSSNameElement name, int lifetime, int usage, - GSSLibStub stub) throws GSSException { + GSSCredElement(GSSNameElement name, String password, 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); + pCred = cStub.acquireCred(this.name.pName, password, lifetime, + usage); } else { - pCred = cStub.acquireCred(0, lifetime, usage); + pCred = cStub.acquireCred(0, password, lifetime, usage); this.name = new GSSNameElement(cStub.getCredName(pCred), cStub.getMech(), cStub); doServicePermCheck(); } } + GSSCredElement(GSSNameElement name, int lifetime, int usage, + GSSLibStub stub) throws GSSException { + this(name, (String)null, lifetime, usage, stub); + } + public Provider getProvider() { return SunNativeProvider.INSTANCE; } diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSLibStub.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSLibStub.java index cbdf7a3b591..d6089781b28 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSLibStub.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSLibStub.java @@ -69,8 +69,8 @@ class GSSLibStub { native Object[] displayName(long pName) throws GSSException; // Credential related routines - native long acquireCred(long pName, int lifetime, int usage) - throws GSSException; + native long acquireCred(long pName, String password, + int lifetime, int usage) throws GSSException; native long releaseCred(long pCred); native long getCredName(long pCred); native int getCredTime(long pCred); diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSFactory.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSFactory.java index 89012727989..66dd30660c8 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSFactory.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSFactory.java @@ -96,6 +96,7 @@ public GSSNameSpi getNameElement(byte[] name, Oid nameType) } public GSSCredentialSpi getCredentialElement(GSSNameSpi name, + String password, int initLifetime, int acceptLifetime, int usage) @@ -118,14 +119,26 @@ public GSSCredentialSpi getCredentialElement(GSSNameSpi name, if (credElement == null) { // No cred in the Subject if (usage == GSSCredential.INITIATE_ONLY) { - credElement = new GSSCredElement(nname, initLifetime, - usage, cStub); + if (password == null) { + credElement = new GSSCredElement(nname, initLifetime, + usage, cStub); + } else { + credElement = new GSSCredElement(nname, password, + initLifetime, + usage, cStub); + } } else if (usage == GSSCredential.ACCEPT_ONLY) { if (nname == null) { nname = GSSNameElement.DEF_ACCEPTOR; } - credElement = new GSSCredElement(nname, acceptLifetime, - usage, cStub); + if (password == null) { + credElement = new GSSCredElement(nname, acceptLifetime, + usage, cStub); + } else { + credElement = new GSSCredElement(nname, password, + acceptLifetime, + usage, cStub); + } } else { throw new GSSException(GSSException.FAILURE, -1, "Unknown usage mode requested"); @@ -134,6 +147,15 @@ public GSSCredentialSpi getCredentialElement(GSSNameSpi name, return credElement; } + public GSSCredentialSpi getCredentialElement(GSSNameSpi name, + int initLifetime, + int acceptLifetime, + int usage) + throws GSSException { + return getCredentialElement(name, null, initLifetime, + acceptLifetime, usage); + } + public GSSContextSpi getMechanismContext(GSSNameSpi peer, GSSCredentialSpi myCred, int lifetime) diff --git a/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c b/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c index cd8547a89ce..c5ba7368213 100644 --- a/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c +++ b/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c @@ -296,7 +296,7 @@ JNIEXPORT jobjectArray JNICALL Java_sun_security_jgss_wrapper_GSSLibStub_inquireNamesForMech(JNIEnv *env, jobject jobj) { - OM_uint32 minor, major; + OM_uint32 minor, major, dummy; gss_OID mech; gss_OID_set nameTypes; jobjectArray result; @@ -310,7 +310,7 @@ Java_sun_security_jgss_wrapper_GSSLibStub_inquireNamesForMech(JNIEnv *env, /* release intermediate buffers before checking status */ result = getJavaOIDArray(env, nameTypes); - deleteGSSOIDSet(nameTypes); + (*ftab->releaseOidSet)(&dummy, &nameTypes); if ((*env)->ExceptionCheck(env)) { return NULL; } @@ -641,17 +641,19 @@ Java_sun_security_jgss_wrapper_GSSLibStub_displayName(JNIEnv *env, /* * Class: sun_security_jgss_wrapper_GSSLibStub * Method: acquireCred - * Signature: (JII)J + * Signature: (JLjava/lang/String;II)J */ JNIEXPORT jlong JNICALL Java_sun_security_jgss_wrapper_GSSLibStub_acquireCred(JNIEnv *env, jobject jobj, jlong pName, + jstring jPassword, jint reqTime, jint usage) { OM_uint32 minor, major; gss_OID mech; + gss_OID_set_desc singleton; gss_OID_set mechs; gss_cred_usage_t credUsage; gss_name_t nameHdl; @@ -661,7 +663,6 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acquireCred(JNIEnv *env, TRACE0("[GSSLibStub_acquireCred]"); mech = (gss_OID) jlong_to_ptr((*env)->GetLongField(env, jobj, FID_GSSLibStub_pMech)); - mechs = newGSSOIDSet(mech); credUsage = (gss_cred_usage_t) usage; nameHdl = (gss_name_t) jlong_to_ptr(pName); @@ -669,11 +670,29 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acquireCred(JNIEnv *env, /* gss_acquire_cred(...) => GSS_S_BAD_MECH, GSS_S_BAD_NAMETYPE, GSS_S_BAD_NAME, GSS_S_CREDENTIALS_EXPIRED, GSS_S_NO_CRED */ - major = - (*ftab->acquireCred)(&minor, nameHdl, reqTime, mechs, - credUsage, &credHdl, NULL, NULL); - /* release intermediate buffers */ - deleteGSSOIDSet(mechs); + if (jPassword == NULL) { + mechs = makeGSSOIDSet(&singleton, mech); + major = (*ftab->acquireCred)(&minor, nameHdl, reqTime, mechs, credUsage, + &credHdl, NULL, NULL); + } else { + gss_buffer_desc password; + + if (ftab->acquireCredWithPassword == NULL) { + const char *msg = "GSSLibStub_acquireCred with password not supported " + "by GSS provider"; + + TRACE0(msg); + checkStatus(env, jobj, GSS_S_UNAVAILABLE, minor=0, msg); + return ptr_to_jlong(NULL); + } + + initGSSBufferString(env, jPassword, &password); + mechs = makeGSSOIDSet(&singleton, mech); + major = (*ftab->acquireCredWithPassword)(&minor, nameHdl, &password, + reqTime, mechs, credUsage, + &credHdl, NULL, NULL); + resetGSSBufferString(env, jPassword, &password); + } TRACE1("[GSSLibStub_acquireCred] pCred=%" PRIuPTR "", (uintptr_t) credHdl); diff --git a/src/java.security.jgss/share/native/libj2gss/NativeFunc.c b/src/java.security.jgss/share/native/libj2gss/NativeFunc.c index 69b3b9cdfe7..650520d1732 100644 --- a/src/java.security.jgss/share/native/libj2gss/NativeFunc.c +++ b/src/java.security.jgss/share/native/libj2gss/NativeFunc.c @@ -36,6 +36,7 @@ static const char EXPORT_NAME[] = "gss_export_name"; static const char DISPLAY_NAME[] = "gss_display_name"; static const char LOCAL_NAME[] = "gss_localname"; static const char ACQUIRE_CRED[] = "gss_acquire_cred"; +static const char ACQUIRE_CRED_WITH_PASSWORD[] = "gss_acquire_cred_with_password"; static const char RELEASE_CRED[] = "gss_release_cred"; static const char INQUIRE_CRED[] = "gss_inquire_cred"; static const char IMPORT_SEC_CONTEXT[] = "gss_import_sec_context"; @@ -135,6 +136,13 @@ int loadNative(const char *libName) { goto out; } + /* + * This one may not be available for a given GSS library, as it's an + * extension, therefore we don't fail if it's missing. + */ + ftab->acquireCredWithPassword = (ACQUIRE_CRED_WITH_PASSWORD_FN_PTR) + GETFUNC(gssLib, ACQUIRE_CRED_WITH_PASSWORD); + ftab->releaseCred = (RELEASE_CRED_FN_PTR)GETFUNC(gssLib, RELEASE_CRED); if (ftab->releaseCred == NULL) { failed = TRUE; diff --git a/src/java.security.jgss/share/native/libj2gss/NativeFunc.h b/src/java.security.jgss/share/native/libj2gss/NativeFunc.h index 7aeaaea4f8a..5b65ee742c3 100644 --- a/src/java.security.jgss/share/native/libj2gss/NativeFunc.h +++ b/src/java.security.jgss/share/native/libj2gss/NativeFunc.h @@ -100,6 +100,17 @@ typedef OM_uint32 (*ACQUIRE_CRED_FN_PTR) gss_OID_set *actual_mechs, OM_uint32 *time_rec); +typedef OM_uint32 (*ACQUIRE_CRED_WITH_PASSWORD_FN_PTR) + (OM_uint32 *minor_status, + gss_name_t desired_name, + gss_buffer_t password, + OM_uint32 time_req, + gss_OID_set desired_mech, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec); + typedef OM_uint32 (*RELEASE_CRED_FN_PTR) (OM_uint32 *minor_status, gss_cred_id_t *cred_handle); @@ -257,6 +268,7 @@ typedef struct GSS_FUNCTION_TABLE { DISPLAY_NAME_FN_PTR displayName; LOCAL_NAME_FN_PTR localName; ACQUIRE_CRED_FN_PTR acquireCred; + ACQUIRE_CRED_WITH_PASSWORD_FN_PTR acquireCredWithPassword; RELEASE_CRED_FN_PTR releaseCred; INQUIRE_CRED_FN_PTR inquireCred; IMPORT_SEC_CONTEXT_FN_PTR importSecContext; diff --git a/src/java.security.jgss/share/native/libj2gss/NativeUtil.c b/src/java.security.jgss/share/native/libj2gss/NativeUtil.c index 805420575a6..fab2c007bb4 100644 --- a/src/java.security.jgss/share/native/libj2gss/NativeUtil.c +++ b/src/java.security.jgss/share/native/libj2gss/NativeUtil.c @@ -802,37 +802,18 @@ jobject getJavaOID(JNIEnv *env, gss_OID cOid) { return result; } /* - * Utility routine for creating a gss_OID_set structure - * using the specified gss_OID. - * NOTE: need to call deleteGSSOIDSet(...) afterwards - * to release the created gss_OID_set structure. + * Utility routine for filling in a 1-element gss_OID_set structure using the + * specified gss_OID (storage owned by caller). However, with SPNEGO we return + * a static set containing all the available mechanisms. */ -gss_OID_set newGSSOIDSet(gss_OID oid) { - gss_OID_set oidSet; - OM_uint32 minor; // don't care; just so it compiles - - if (oid->length != 6 || - memcmp(oid->elements, SPNEGO_BYTES, 6) != 0) { - (*ftab->createEmptyOidSet)(&minor, &oidSet); - (*ftab->addOidSetMember)(&minor, oid, &oidSet); - return oidSet; - } else { - // Use all mechs for SPNEGO in order to work with - // various native GSS impls - return (ftab->mechs); - } -} -/* - * Utility routine for releasing a gss_OID_set structure. - * NOTE: used in conjunction with newGSSOIDSet(...). - */ -void deleteGSSOIDSet(gss_OID_set oidSet) { - OM_uint32 minor; /* don't care; just so it compiles */ - - if ((oidSet != ftab->mechs) && - (oidSet != NULL) && (oidSet != GSS_C_NO_OID_SET)) { - (*ftab->releaseOidSet)(&minor, &oidSet); - } +gss_OID_set makeGSSOIDSet(gss_OID_set mechs, gss_OID oid) { + if (oid->length != 6 || memcmp(oid->elements, SPNEGO_BYTES, 6) != 0) { + mechs->count = 1; + mechs->elements = oid; + return mechs; + } + /* Use all mechs for SPNEGO in order to work with various native GSS impls */ + return (ftab->mechs); } /* * Utility routine for creating a org.ietf.jgss.Oid[] diff --git a/src/java.security.jgss/share/native/libj2gss/NativeUtil.h b/src/java.security.jgss/share/native/libj2gss/NativeUtil.h index 9f30519ce90..5a3a11e08ee 100644 --- a/src/java.security.jgss/share/native/libj2gss/NativeUtil.h +++ b/src/java.security.jgss/share/native/libj2gss/NativeUtil.h @@ -46,8 +46,7 @@ extern "C" { extern gss_OID newGSSOID(JNIEnv *, jobject); extern void deleteGSSOID(gss_OID); - extern gss_OID_set newGSSOIDSet(gss_OID); - extern void deleteGSSOIDSet(gss_OID_set); + extern gss_OID_set makeGSSOIDSet(gss_OID_set, gss_OID); extern jbyteArray getJavaBuffer(JNIEnv *, gss_buffer_t, jboolean); extern jstring getJavaString(JNIEnv *, gss_buffer_t); diff --git a/src/java.security.jgss/share/native/libj2gss/gssapi.h b/src/java.security.jgss/share/native/libj2gss/gssapi.h index 7acdead0320..c1838161bf1 100644 --- a/src/java.security.jgss/share/native/libj2gss/gssapi.h +++ b/src/java.security.jgss/share/native/libj2gss/gssapi.h @@ -686,6 +686,23 @@ GSS_DLLIMP OM_uint32 gss_canonicalize_name( # pragma pack(pop) #endif +/* Common extension (NOT in RFC2744) */ +OM_uint32 +gss_add_cred_with_password( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* input_cred_handle */ + const gss_name_t, /* desired_name */ + const gss_OID, /* desired_mech */ + const gss_buffer_t, /* password */ + gss_cred_usage_t, /* cred_usage */ + OM_uint32, /* initiator_time_req */ + OM_uint32, /* acceptor_time_req */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *, /* initiator_time_rec */ + OM_uint32 * /* acceptor_time_rec */ +); + /* Common extension for aname2lname (NOT in RFC2744) */ OM_uint32 gss_localname( From d975a3d961bc00d40663132367177abb1d765f52 Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Thu, 16 Aug 2018 11:52:04 -0500 Subject: [PATCH 10/25] JGSS: Don't dispose() of creds too eagerly We must not dispose() of any credential handle passed to the NativeGSSContext() constructor. But we must dispose() of credentials that are acquired in NativeGSSContext. This is very important because the JVM does not know about the size of the JNI credential objects, so it doesn't readily recognize memory pressure from them, leading to memory pressure issues in SASL and GSS server applications. --- .../jgss/wrapper/NativeGSSContext.java | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java index 1d16256e304..08d0b5dd978 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java @@ -63,6 +63,7 @@ class NativeGSSContext implements GSSContextSpi { private GSSNameElement srcName; private GSSNameElement targetName; private GSSCredElement cred; + private GSSCredElement disposeCred; private boolean isInitiator; private boolean isEstablished; private Oid actualMech; // Assigned during context establishment @@ -192,6 +193,7 @@ private byte[] retrieveToken(InputStream is, int mechTokenLen) } cStub = stub; cred = myCred; + disposeCred = null; targetName = peer; isInitiator = true; lifetime = time; @@ -199,8 +201,9 @@ private byte[] retrieveToken(InputStream is, int mechTokenLen) if (GSSUtil.isKerberosMech(cStub.getMech())) { doServicePermCheck(); if (cred == null) { - cred = new GSSCredElement(null, lifetime, - GSSCredential.INITIATE_ONLY, cStub); + disposeCred = cred = + new GSSCredElement(null, lifetime, + GSSCredential.INITIATE_ONLY, cStub); } srcName = cred.getName(); } @@ -211,6 +214,7 @@ private byte[] retrieveToken(InputStream is, int mechTokenLen) throws GSSException { cStub = stub; cred = myCred; + disposeCred = null; if (cred != null) targetName = cred.getName(); @@ -298,9 +302,9 @@ public byte[] initSecContext(InputStream is, int mechTokenLen) cStub); } if (cred == null) { - cred = new GSSCredElement(srcName, lifetime, - GSSCredential.INITIATE_ONLY, - cStub); + disposeCred = cred = + new GSSCredElement(srcName, lifetime, + GSSCredential.INITIATE_ONLY, cStub); } } } @@ -324,9 +328,11 @@ public byte[] acceptSecContext(InputStream is, int mechTokenLen) (cStub.getContextName(pContext, false), actualMech, 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); + if (disposeCred != null) + disposeCred.dispose(); + disposeCred = cred = + new GSSCredElement(targetName, lifetime, + GSSCredential.ACCEPT_ONLY, cStub); } // Only inspect token when the permission check has not @@ -347,9 +353,11 @@ public boolean isEstablished() { } public void dispose() throws GSSException { + if (disposeCred != null) + disposeCred.dispose(); + disposeCred = cred = null; srcName = null; targetName = null; - cred = null; delegatedCred = null; if (pContext != 0) { pContext = cStub.deleteContext(pContext); From 561e7f41910ff94985e9eb2c7bb3a4315da2342b Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Thu, 16 Aug 2018 11:53:09 -0500 Subject: [PATCH 11/25] Fix SpNego multi-round-trip bug There is only one token that we can extract an actual mechanism OID from in the SPNEGO case when the native GSS library doesn't provide that (though it should) in the API. If the SPNEGO exchange ends up requiring more than two tokens, then the previous code failed to establish a security context. Also, never raise if we cannot get an actual mech OID from SPNEGO tokens. --- .../jgss/wrapper/NativeGSSContext.java | 46 +++++++++++++++---- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java index 08d0b5dd978..3b1f0430dd5 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java @@ -127,6 +127,8 @@ private void doServicePermCheck() throws GSSException { Krb5Util.checkServicePermission(tgsName, action); } } + if (targetName == null) + return; String targetStr = targetName.getKrbName(); Krb5Util.checkServicePermission(targetStr, action); skipServicePermCheck = true; @@ -137,6 +139,8 @@ private void doServicePermCheck() throws GSSException { private void doDelegPermCheck() throws GSSException { SecurityManager sm = System.getSecurityManager(); if (sm != null) { + if (targetName == null) + return; String targetStr = targetName.getKrbName(); String tgsStr = Krb5Util.getTGSName(targetName); StringBuilder sb = new StringBuilder("\""); @@ -240,12 +244,17 @@ private byte[] retrieveToken(InputStream is, int mechTokenLen) if (info.length != NUM_OF_INQUIRE_VALUES) { throw new RuntimeException("Bug w/ GSSLibStub.inquireContext()"); } - srcName = new GSSNameElement(info[0], actualMech, cStub); - targetName = new GSSNameElement(info[1], actualMech, cStub); isInitiator = (info[2] != 0); isEstablished = (info[3] != 0); flags = (int) info[4]; lifetime = (int) info[5]; + if (isEstablished) { + srcName = new GSSNameElement(info[0], actualMech, cStub); + targetName = new GSSNameElement(info[1], actualMech, cStub); + } else { + srcName = null; + targetName = null; + } // Do Service Permission check when importing SPNEGO context // just to be safe @@ -285,22 +294,38 @@ public byte[] initSecContext(InputStream is, int mechTokenLen) // Only inspect the token when the permission check // has not been performed - if (GSSUtil.isSpNegoMech(cStub.getMech()) && outToken != null) { + if (!GSSUtil.isSpNegoMech(cStub.getMech())) { + actualMech = cStub.getMech(); + } else if (actualMech == null && outToken != null) { // WORKAROUND for SEAM bug#6287358 - actualMech = getMechFromSpNegoToken(outToken, true); + // + // This is where some C GSS SPNEGO implementations fail to make + // the real actual mechanism available. + // getMechFromSpNegoToken() does the horrible, no good, very + // bad thing its name says it does. For now we retain this bit + // of evil. + try { + actualMech = getMechFromSpNegoToken(outToken, true); + } catch (GSSException e) { } + } - if (GSSUtil.isKerberosMech(actualMech)) { - if (!skipServicePermCheck) doServicePermCheck(); - if (!skipDelegPermCheck) doDelegPermCheck(); - } + if (actualMech != null && GSSUtil.isKerberosMech(actualMech)) { + if (!skipServicePermCheck) doServicePermCheck(); + if (!skipDelegPermCheck) doDelegPermCheck(); } if (isEstablished) { + // XXX We should attempt to get actualMech from the cStub here, + // and take it even if we got a semblance of an actualMech from + // the SPNEGO token, as long as the one returned by the cStub + // isn't the SPNEGO OID. if (srcName == null) { srcName = new GSSNameElement (cStub.getContextName(pContext, true), actualMech, cStub); } + // XXX Attempt to get the actual target name from the cStub. + if (cred == null) { disposeCred = cred = new GSSCredElement(srcName, lifetime, @@ -323,7 +348,7 @@ public byte[] acceptSecContext(InputStream is, int mechTokenLen) SunNativeProvider.debug("acceptSecContext=> outToken len=" + (outToken == null? 0 : outToken.length)); - if (targetName == null) { + if (isEstablished && targetName == null) { targetName = new GSSNameElement (cStub.getContextName(pContext, false), actualMech, cStub); // Replace the current default acceptor cred now that @@ -337,6 +362,9 @@ public byte[] acceptSecContext(InputStream is, int mechTokenLen) // Only inspect token when the permission check has not // been performed + // + // XXX No, we should inspect the token the one time we can, just + // like with actualMech on the initiator side. if (GSSUtil.isSpNegoMech(cStub.getMech()) && (outToken != null) && !skipServicePermCheck) { if (GSSUtil.isKerberosMech(getMechFromSpNegoToken From 5840bfdcbb217cf88a838e99e8f9ef1f46a4f737 Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Wed, 15 Aug 2018 15:21:26 -0500 Subject: [PATCH 12/25] JGSS: Add comment about unnec. perm check --- .../classes/sun/security/jgss/wrapper/NativeGSSContext.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java index 3b1f0430dd5..440f30c7a4f 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java @@ -257,7 +257,9 @@ private byte[] retrieveToken(InputStream is, int mechTokenLen) } // Do Service Permission check when importing SPNEGO context - // just to be safe + // just to be safe. WAT, no. If the caller has an exported sec + // context token, it's because someone gave it to it, therefore there's + // no need to do any further permission checking. REMOVE! Oid mech = cStub.getMech(); if (GSSUtil.isSpNegoMech(mech) || GSSUtil.isKerberosMech(mech)) { doServicePermCheck(); From ac1f7063f462ac192e49a8d7266a7d447832d040 Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Fri, 4 Dec 2015 23:01:02 +0000 Subject: [PATCH 13/25] ServicePermission empty realm support Also use empty realm as wildcard for krbtgt names --- .../auth/kerberos/ServicePermission.java | 46 +++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/ServicePermission.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/ServicePermission.java index 19629b06a86..10e24399c8b 100644 --- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/ServicePermission.java +++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/ServicePermission.java @@ -214,10 +214,48 @@ public boolean implies(Permission p) { boolean impliesIgnoreMask(ServicePermission p) { - return ((this.getName().equals("*")) || - this.getName().equals(p.getName()) || - (p.getName().startsWith("@") && - this.getName().endsWith(p.getName()))); + if ((this.getName().equals("*")) || + this.getName().equals(p.getName()) || + (p.getName().startsWith("@") && + this.getName().endsWith(p.getName()))) + return true; + + /* + * Empty realm in this or p is a wild-card. This is needed to support + * non-Kerberos ServicePermissions for GSS (a band-aid until we can + * implement a proper GssAcceptorPermission class), but also because + * users may not know and might not care what realm the service is in, + * especially when they are using a keytab. + * + * If the user is using a password, then the realm matters more. An + * untrusted actor could cause KDCs for a realm they control to see + * material they could attack offline, but that was already the case + * anyways, and the answer is the same in all cases: use stronger + * passwords, use randomized keys in a keytab, or let us implement + * SPAKE or similar alternatives to the venerable PA-ENC-TIMESTAMP. + */ + if ((this.getName().equals("krbtgt/@") && + p.getName().startsWith("krbtgt/")) || + (p.getName().equals("krbtgt/@") && + this.getName().startsWith("krbtgt/"))) + return true; + + char[] n = this.getName().toCharArray(); + int i; + for (i = 0; i < n.length; i++) { + if (n[i] == '\\') { + i++; + continue; + } + if (n[i] == '@') { + String s = new String(n, 0, i + 1); + return (p.getName().startsWith(s) && + (p.getName().equals(s) || this.getName().equals(s))); + } + } + + // No realm, not even empty -> fail + return false; } /** From 837a750d9b6634d1fab5a683d09d8ab463cd210e Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Fri, 4 Dec 2015 23:14:17 +0000 Subject: [PATCH 14/25] Add isDefaultCredential() method to GSSCredentialSpi --- .../security/jgss/krb5/Krb5AcceptCredential.java | 12 ++++++++++++ .../security/jgss/krb5/Krb5InitCredential.java | 15 ++++++++++++++- .../sun/security/jgss/krb5/Krb5NameElement.java | 4 ++++ .../security/jgss/krb5/Krb5ProxyCredential.java | 5 +++++ .../sun/security/jgss/spi/GSSCredentialSpi.java | 7 +++++++ .../classes/sun/security/jgss/spi/GSSNameSpi.java | 6 ++++++ .../security/jgss/spnego/SpNegoCredElement.java | 4 ++++ .../sun/security/jgss/wrapper/GSSCredElement.java | 8 ++++++++ .../sun/security/jgss/wrapper/GSSNameElement.java | 4 ++++ 9 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java index 262debcaf40..13e5b24f196 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java @@ -47,6 +47,7 @@ public class Krb5AcceptCredential private final Krb5NameElement name; private final ServiceCreds screds; + private boolean isDefCred = false; private Krb5AcceptCredential(Krb5NameElement name, ServiceCreds creds) { /* @@ -57,6 +58,8 @@ private Krb5AcceptCredential(Krb5NameElement name, ServiceCreds creds) { this.name = name; this.screds = creds; + if (name == null) + isDefCred = true; } static Krb5AcceptCredential getInstance(final GSSCaller caller, Krb5NameElement name) @@ -148,6 +151,15 @@ public final Oid getMechanism() { return Krb5MechFactory.GSS_KRB5_MECH_OID; } + /** + * Returns true if the credential is a default credential. + * + * @return true if the credential is a default credential, else false. + */ + public boolean isDefaultCredential() { + return isDefCred; + } + public final java.security.Provider getProvider() { return Krb5MechFactory.PROVIDER; } diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java index 44a0c992a98..ed518b9a381 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java @@ -55,6 +55,7 @@ public class Krb5InitCredential private Krb5NameElement name; private Credentials krb5Credentials; + private boolean isDefCred; private Krb5InitCredential(Krb5NameElement name, byte[] asn1Encoding, @@ -82,6 +83,8 @@ private Krb5InitCredential(Krb5NameElement name, clientAddresses); this.name = name; + if (name == null) + isDefCred = true; try { // Cache this for later use by the sun.security.krb5 package. @@ -134,7 +137,8 @@ private Krb5InitCredential(Krb5NameElement name, this.name = name; // A delegated cred does not have all fields set. So do not try to - // creat new Credentials out of the delegatedCred. + // creat new Credentials out of the delegatedCred. Also, a delegated + // credential is not a default credential. this.krb5Credentials = delegatedCred; } @@ -271,6 +275,15 @@ public final Oid getMechanism() { return Krb5MechFactory.GSS_KRB5_MECH_OID; } + /** + * Returns true if the credential is a default credential. + * + * @return true if the credential is a default credential, else false. + */ + public boolean isDefaultCredential() { + return isDefCred; + } + public final java.security.Provider getProvider() { return Krb5MechFactory.PROVIDER; } diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5NameElement.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5NameElement.java index 4c207bfc8d5..7d26691e89e 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5NameElement.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5NameElement.java @@ -367,6 +367,10 @@ public boolean isAnonymousName() { return (gssNameType.equals(GSSName.NT_ANONYMOUS)); } + public boolean isDefaultCredentialName() { + return false; + } + public Provider getProvider() { return Krb5MechFactory.PROVIDER; } diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5ProxyCredential.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5ProxyCredential.java index 4c5690b3942..7ef357c1770 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5ProxyCredential.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5ProxyCredential.java @@ -84,6 +84,11 @@ public boolean isAcceptorCredential() throws GSSException { return false; } + @Override + public final boolean isDefaultCredential() { + return false; + } + @Override public final Oid getMechanism() { return Krb5MechFactory.GSS_KRB5_MECH_OID; diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSCredentialSpi.java b/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSCredentialSpi.java index 69646d7a960..c417c9999b0 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSCredentialSpi.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSCredentialSpi.java @@ -88,6 +88,13 @@ public interface GSSCredentialSpi { */ public boolean isAcceptorCredential() throws GSSException; + /** + * Returns true if the credential is a default credential. + * + * @return true if the credential is a default credential, else false. + */ + public boolean isDefaultCredential(); + /** * Returns the oid representing the underlying credential * mechanism oid. diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSNameSpi.java b/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSNameSpi.java index bfb93f93719..ed4af830001 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSNameSpi.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/spi/GSSNameSpi.java @@ -129,4 +129,10 @@ public interface GSSNameSpi { * Indicates if this name object represents an Anonymous name. */ public boolean isAnonymousName(); + + /** + * Indicates whether this name object refers to whatever name(s) the + * default credentials respond to. + */ + public boolean isDefaultCredentialName(); } diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoCredElement.java b/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoCredElement.java index c7cf9477424..a832fcc3079 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoCredElement.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoCredElement.java @@ -91,4 +91,8 @@ public Oid getMechanism() { public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException { return cred.impersonate(name); } + + public boolean isDefaultCredential() { + return cred.isDefaultCredential(); + } } diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java index d457baf6e57..5d2b7e6486d 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java @@ -42,6 +42,7 @@ public class GSSCredElement implements GSSCredentialSpi { long pCred; // Pointer to the gss_cred_id_t structure private GSSNameElement name = null; private GSSLibStub cStub; + public boolean isDefCred; // Perform the necessary ServicePermission check on this cred void doServicePermCheck() throws GSSException { @@ -79,10 +80,13 @@ void doServicePermCheck() throws GSSException { doServicePermCheck(); pCred = cStub.acquireCred(this.name.pName, password, lifetime, usage); + if (name == GSSNameElement.DEF_ACCEPTOR) + isDefCred = true; } else { pCred = cStub.acquireCred(0, password, lifetime, usage); this.name = new GSSNameElement(cStub.getCredName(pCred), cStub.getMech(), cStub); doServicePermCheck(); + isDefCred = true; } } @@ -131,6 +135,10 @@ public Oid getMechanism() { return cStub.getMech(); } + public boolean isDefaultCredential() { + return isDefCred; + } + public String toString() { // No hex bytes available for native impl return "N/A"; diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java index 9952ee12d17..d066de5e961 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java @@ -294,6 +294,10 @@ public boolean isAnonymousName() { return (GSSName.NT_ANONYMOUS.equals(printableType)); } + public boolean isDefaultCredentialName() { + return (this == DEF_ACCEPTOR); + } + public void dispose() { if (pName != 0) { cStub.releaseName(pName); From 173c04bedd32c9cdde8a58e4bb2f51f15030febc Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Fri, 4 Dec 2015 23:19:50 +0000 Subject: [PATCH 15/25] JGSS: Prefer default cred handles if possible --- .../classes/sun/security/jgss/GSSUtil.java | 56 ++++++++++++------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/GSSUtil.java b/src/java.security.jgss/share/classes/sun/security/jgss/GSSUtil.java index 936883c9c67..4b5e57ed1c2 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/GSSUtil.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/GSSUtil.java @@ -321,30 +321,44 @@ public static boolean useMSInterop() { public Vector run() throws Exception { Subject accSubj = Subject.getSubject(acc); Vector result = null; - if (accSubj != null) { - result = new Vector(); - Iterator 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()))) { + if (accSubj == null) { + debug("No Subject"); + return result; + } + + result = new Vector(); + Iterator 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)) { + debug("......Discard element (class mismatch)"); + } else if (name == null) { + /* + * If the caller doesn't care about + * the specific name, then prefer + * default credentials to + * non-default credentials. + */ + if (ce.isDefaultCredential()) + result.add(0, credCls.cast(ce)); + else result.add(credCls.cast(ce)); - } else { - debug("......Discard element"); - } - } catch (GSSException ge) { - debug("...Discard cred (" + ge + ")"); + } else if (name.equals((Object) ce.getName())) { + result.add(credCls.cast(ce)); + } else { + debug("......Discard element (name mismatch)"); } + } catch (GSSException ge) { + debug("...Discard cred (exception: " + ge + ")"); } - } else debug("No Subject"); + } return result; } }); From b7cd095737a49693508da29835b97affe8ca24fd Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Fri, 4 Dec 2015 23:27:22 +0000 Subject: [PATCH 16/25] Make GSSName implement Principal (add getName()) --- .../share/classes/org/ietf/jgss/GSSName.java | 23 ++++++++++++++++- .../sun/security/jgss/GSSNameImpl.java | 14 +++++++++++ .../classes/sun/security/jgss/GSSUtil.java | 25 +++---------------- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/src/java.security.jgss/share/classes/org/ietf/jgss/GSSName.java b/src/java.security.jgss/share/classes/org/ietf/jgss/GSSName.java index f0bd258b55f..d0013762ca8 100644 --- a/src/java.security.jgss/share/classes/org/ietf/jgss/GSSName.java +++ b/src/java.security.jgss/share/classes/org/ietf/jgss/GSSName.java @@ -24,6 +24,7 @@ */ package org.ietf.jgss; +import java.security.Principal; /** * This interface encapsulates a single GSS-API principal entity. The @@ -102,7 +103,7 @@ * @author Mayank Upadhyay * @since 1.4 */ -public interface GSSName { +public interface GSSName extends Principal { /** * Oid indicating a host-based service name form. It is used to @@ -281,6 +282,26 @@ public interface GSSName { */ public String toString(); + /** + * Returns a textual representation of the GSSName object. + * + * If this is not an MN then the returned name should be the + * same as the generic name used to construct it. Otherwise the returned + * name may be a mechanism-specific name string. + * + * @return a String representing this name in printable form. + */ + public String getName(); + + /** + * Returns a textual representation of the GSSName object + * element corresponding to the given mech. This will be a + * mechanism-specific representation of this diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/GSSNameImpl.java b/src/java.security.jgss/share/classes/sun/security/jgss/GSSNameImpl.java index 97badb1e71c..01f39816266 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/GSSNameImpl.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/GSSNameImpl.java @@ -450,6 +450,20 @@ public String toString() { return printableName; } + public String getName() { + return printableName; + } + + public String getName(Oid mech) throws GSSException { + GSSNameSpi element = elements.get(mech); + if (element == null) { + throw new GSSExceptionImpl(GSSException.UNAVAILABLE, + "GSSName object does not have an element for the " + + "given mechanism"); + } + return element.toString(); + } + public String getLocalName() throws GSSException { String lname = null; Oid mech = null; diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/GSSUtil.java b/src/java.security.jgss/share/classes/sun/security/jgss/GSSUtil.java index 4b5e57ed1c2..4c1df31e9de 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/GSSUtil.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/GSSUtil.java @@ -124,24 +124,8 @@ public static Subject getSubject(GSSName name, Set gssCredentials = null; - Set krb5Principals = - new HashSet(); - - 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); - } - } + Set names = new HashSet(); + names.add(name); if (creds instanceof GSSCredentialImpl) { gssCredentials = ((GSSCredentialImpl) creds).getElements(); @@ -151,12 +135,11 @@ public static Subject getSubject(GSSName name, privCredentials = new HashSet(); // empty Set } debug("Created Subject with the following"); - debug("principals=" + krb5Principals); + debug("principals=" + names); debug("public creds=" + pubCredentials); debug("private creds=" + privCredentials); - return new Subject(false, krb5Principals, pubCredentials, - privCredentials); + return new Subject(false, names, pubCredentials, privCredentials); } From 89341f8c4d10e670fa62820a6d67e4d624ccf843 Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Wed, 7 Oct 2015 21:18:59 -0400 Subject: [PATCH 17/25] Add GssLoginModule This module is to be used for GSS applications in preference to Krb5LoginModule, especially when using the native GSS provider. --- .../sun/security/jgss/LoginConfigImpl.java | 3 + .../security/auth/module/GssLoginModule.java | 607 ++++++++++++++++++ .../sun/security/jgss/DefaultGssConfig.java | 4 + 3 files changed, 614 insertions(+) create mode 100644 src/jdk.security.auth/share/classes/com/sun/security/auth/module/GssLoginModule.java diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java b/src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java index c2b07ecfa79..4d543044cc4 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java @@ -185,6 +185,9 @@ private AppConfigurationEntry[] getDefaultConfigurationEntry() { options.put("doNotPrompt", "false"); } return new AppConfigurationEntry[] { + // FIXME Also -or alternatively when we're using the native + // providers- add GssLoginModule, since we might not be able to + // acquire Kerberos credentials directly, or even at all . new AppConfigurationEntry( "com.sun.security.auth.module.Krb5LoginModule", AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, diff --git a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/GssLoginModule.java b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/GssLoginModule.java new file mode 100644 index 00000000000..b3253b679df --- /dev/null +++ b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/GssLoginModule.java @@ -0,0 +1,607 @@ +/* + * Copyright (c) 2000, 2017-2018 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 com.sun.security.auth.module; + +import java.io.*; +import java.text.MessageFormat; +import java.util.*; +import javax.security.auth.*; +import javax.security.auth.callback.*; +import javax.security.auth.login.*; +import javax.security.auth.spi.*; + +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSCredential; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; + +import static sun.security.util.ResourcesMgr.getAuthResourceString; + +/** + *

This LoginModule authenticates users using the + * GSS-API.

+ */ + +public class GssLoginModule implements LoginModule { + + // From initialize + private Subject subject; + private CallbackHandler callbackHandler; + private Map sharedState; + private Map options; + + // Configuration option + private boolean debug; + private boolean doNotPrompt; + private String defName; + private String name; + private String nametype; // username, hostbased, unspecified + private Oid nametypeOid; + + private GSSManager manager; + private GSSName gssName; + private GSSCredential gssICred; + private GSSCredential gssACred; + + private boolean useFirstPass; + private boolean tryFirstPass; + private boolean storePass; + private boolean clearPass; + private boolean initiate; + private boolean accept; + private boolean tryDefaultCreds; + private boolean useDefaultCreds; + + // Module state + private boolean succeeded; + private boolean commitSucceeded; + + private String password; + + private static final String NAME = "javax.security.auth.login.name"; + private static final String PWD = "javax.security.auth.login.password"; + + private String getWithDefault(String key, String defval) { + String value = (String)options.get(key); + return value != null ? value : defval; + } + + /** + * Initialize this LoginModule. + * + *

+ * @param subject the Subject to be authenticated.

+ * + *

+ * @param callbackHandler a CallbackHandler for + * communication with the end user (prompting for + * usernames and passwords, for example).

+ * + *

+ * @param sharedState shared LoginModule state.

+ * + *

+ * @param options options specified in the login + * Configuration for this particular + * LoginModule.

+ */ + // Unchecked warning from (Map)sharedState is safe + // since javax.security.auth.login.LoginContext passes a raw HashMap. + // Unchecked warnings from options.get(String) are safe since we are + // passing known keys. + @SuppressWarnings("unchecked") + public void initialize(Subject subject, + CallbackHandler callbackHandler, + Map sharedState, + Map options) { + + this.subject = subject; + this.callbackHandler = callbackHandler; + this.sharedState = (Map)sharedState; + this.options = options; + + manager = GSSManager.getInstance(); + + // initialize any configured options + + debug = "true".equalsIgnoreCase((String)options.get("debug")); + doNotPrompt = + "true".equalsIgnoreCase(getWithDefault("doNotPrompt", "true")); + defName = (String)options.get("name"); + nametype = (String)options.get("nametype"); + + if (defName == null) + defName = System.getProperty("sun.security.gss.name"); + if (nametype == null) + nametype = System.getProperty("sun.security.gss.nametype"); + if (nametype == null || nametype.equals("username")) { + nametypeOid = GSSName.NT_USER_NAME; + } else if (nametype.equals("hostbased")) { + nametypeOid = GSSName.NT_HOSTBASED_SERVICE; + } else if (!nametype.equals("")) { + try { + nametypeOid = new Oid(nametype); + } catch (GSSException e) { + if (debug) + System.out.print("Unknown name type OID " + nametype); + nametypeOid = null; + } + } else { + nametype = ""; + nametypeOid = GSSName.NT_USER_NAME; + } + + tryFirstPass = + "true".equalsIgnoreCase(getWithDefault("tryFirstPass", "true")); + useFirstPass = + "true".equalsIgnoreCase( + getWithDefault("useFirstPass", + doNotPrompt ? "true" : "false")); + storePass = + "true".equalsIgnoreCase((String)options.get("storePass")); + clearPass = + "true".equalsIgnoreCase((String)options.get("clearPass")); + initiate = + "true".equalsIgnoreCase((String)options.get("initiate")); + accept = + "true".equalsIgnoreCase((String)options.get("accept")); + tryDefaultCreds = + "true".equalsIgnoreCase(getWithDefault("tryDefaultCreds", "true")); + useDefaultCreds = + "true".equalsIgnoreCase( + getWithDefault("useDefaultCreds", + doNotPrompt ? "true" : "false")); + if (!initiate && !accept) + initiate = true; + if (debug) { + System.out.print("Debug is " + debug + + " doNotPrompt " + doNotPrompt + + " defName is " + defName + + " nametype is " + nametype + + " tryFirstPass is " + tryFirstPass + + " useFirstPass is " + useFirstPass + + " storePass is " + storePass + + " clearPass is " + clearPass + + " initiate is " + initiate + + " accept is " + accept + + " tryDefaultCreds is " + tryDefaultCreds + + " useDefaultCreds is " + useDefaultCreds + "\n"); + } + } + + + /** + * Authenticate the user + * + *

+ * + * @return true in all cases since this LoginModule + * should not be ignored.

+ * + *

+ * @exception FailedLoginException if the authentication fails.

+ * + *

+ * @exception LoginException if this LoginModule + * is unable to perform the authentication.

+ */ + public boolean login() throws LoginException { + succeeded = false; + try { + if (tryFirstPass || useFirstPass) { + attemptAuthentication(true); + if (debug) + System.out.println("\t\t[GssLoginModule] " + + "authentication succeeded"); + succeeded = true; + cleanState(); + return true; + } + } catch (LoginException le) { + // authentication failed -- try again below by prompting + cleanState(); + if (debug) { + System.out.println("\t\t[GssLoginModule] " + + (tryFirstPass ? "tryFirstPass " : "") + + "authentication failed with:" + + le.getMessage()); + } + if (useFirstPass) + throw le; + } + + // The first password didn't work or we didn't try it, try prompting + try { + attemptAuthentication(false); + if (debug) + System.out.println("\t\t[GssLoginModule] " + + "authentication succeeded"); + succeeded = true; + cleanState(); + return true; + } catch (LoginException le2) { + cleanState(); + if (debug) { + System.out.println("\t\t[GssLoginModule] " + + (tryFirstPass ? "tryFirstPass " : "") + + "authentication failed with:" + + le2.getMessage()); + } + throw le2; + } + } + + private void getcreds() throws GSSException { + if (initiate) { + if (debug) + System.out.println("\t\t[GssLoginModule] acquiring" + + ((gssName == null) ? " default" : "") + + " initiator credentials..."); + gssICred = manager.createCredential(gssName, password, + GSSCredential.DEFAULT_LIFETIME, (Oid[])null, + GSSCredential.INITIATE_ONLY); + if (gssName == null) + gssName = gssICred.getName(); + if (debug) + System.out.println("\t\t[GssLoginModule] acquired" + + " initiator credentials: " + gssName); + } + if (accept) { + if (debug) + System.out.println("\t\t[GssLoginModule] acquiring" + + ((gssName == null) ? " default" : "") + + " acceptor credentials..."); + gssACred = manager.createCredential(gssName, password, + GSSCredential.DEFAULT_LIFETIME, (Oid[])null, + GSSCredential.ACCEPT_ONLY); + // Default acceptor credentials retain a null name + if (debug) + System.out.println("\t\t[GssLoginModule] acquired" + + " acceptor credentials"); + } + } + + private void attemptAuthentication(boolean getPasswdFromSharedState) + throws LoginException { + + // Get a name, maybe + if (name == null) { + if (useDefaultCreds) { + try { + getcreds(); + return; + } catch (GSSException e) { + throw new LoginException(e.getMessage()); + } + } + if (tryDefaultCreds) { + try { + getcreds(); + return; + } catch (GSSException e) { } + } + + promptForName(getPasswdFromSharedState); + if (name == null) + throw new LoginException ("Unable to determine a GSS name"); + } + + try { + gssName = manager.createName(name, nametypeOid); + } catch (GSSException e) { + throw new LoginException ("Unable to import GSS name"); + } + + promptForPass(getPasswdFromSharedState); + + try { + getcreds(); + } catch (GSSException e) { + throw new LoginException(e.getMessage()); + } + } + + private void promptForName(boolean getPasswdFromSharedState) + throws LoginException { + if (getPasswdFromSharedState) { + // use the name saved by a module earlier in the stack + name = (String)sharedState.get(NAME); + if (name == null || name.length() == 0) + name = defName; + if (debug) { + System.out.println("\t\t[GssLoginModule] username from" + + " shared state is " + name); + } + if (name != null && name.length() > 0) + return; + } + + if (doNotPrompt) + return; // name may be null + + if (callbackHandler == null) + throw new LoginException("No CallbackHandler " + + "available " + + "to prompt for authentication " + + "information from the user"); + + try { + String defUsername = System.getProperty("user.name"); + + Callback[] callbacks = new Callback[1]; + MessageFormat form = new MessageFormat( + getAuthResourceString( + "GSS.name.defName.")); + Object[] source = {defUsername}; + callbacks[0] = new NameCallback(form.format(source)); + callbackHandler.handle(callbacks); + name = ((NameCallback)callbacks[0]).getName(); + if (name != null && name.length() == 0) + name = null; + if (name == null && defUsername != null && + defUsername.length() != 0) + name = defUsername; + } catch (java.io.IOException ioe) { + throw new LoginException(ioe.getMessage()); + } catch (UnsupportedCallbackException uce) { + throw new LoginException + (uce.getMessage() + +" not available to garner " + +" authentication information " + +" from the user"); + } + // name may still be null, which we take to mean "use default + // credentials" + } + + private void promptForPass(boolean getPasswdFromSharedState) + throws LoginException { + + char[] pw; + + if (getPasswdFromSharedState) { + // use the password saved by the first module in the stack + pw = (char[])sharedState.get(PWD); + if (pw == null) { + if (debug) + System.out.println("\t\t[GssLoginModule] password from" + + " shared state is null"); + throw new LoginException + ("Password can not be obtained from sharedstate "); + } + password = new String(pw); + return; + } + if (doNotPrompt) + throw new LoginException("Unable to prompt for password\n"); + + if (callbackHandler == null) { + throw new LoginException("No CallbackHandler " + + "available " + + "to garner authentication " + + "information from the user"); + } + try { + Callback[] callbacks = new Callback[1]; + MessageFormat form = new MessageFormat( + getAuthResourceString( + "Kerberos.password.for.username.")); + Object[] source = {name}; + callbacks[0] = new PasswordCallback(form.format(source), false); + callbackHandler.handle(callbacks); + char[] tmpPassword = ((PasswordCallback) + callbacks[0]).getPassword(); + if (tmpPassword == null) + throw new LoginException("No password provided"); + password = new String(tmpPassword); + ((PasswordCallback)callbacks[0]).clearPassword(); + + // clear tmpPassword + for (int i = 0; i < tmpPassword.length; i++) + tmpPassword[i] = ' '; + } catch (java.io.IOException ioe) { + throw new LoginException(ioe.getMessage()); + } catch (UnsupportedCallbackException uce) { + throw new LoginException(uce.getMessage() + +" not available to garner " + +" authentication information " + + "from the user"); + } + } + + /** + *

This method is called if the LoginContext's + * overall authentication succeeded + * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL + * LoginModules succeeded).

+ * + *

If this LoginModule's own authentication attempt + * succeeded (checked by retrieving the private state saved by the + * login method), then this method associates a + * GSSName + * with the Subject located in the + * LoginModule. It adds GSS Credentials to the + * the Subject's private credentials set. If this LoginModule's own + * authentication attempted failed, then this method removes + * any state that was originally saved.

+ * + *

+ * + * @exception LoginException if the commit fails.

+ * + *

+ * @return true if this LoginModule's own login and commit + * attempts succeeded, or false otherwise.

+ */ + + public boolean commit() throws LoginException { + if (succeeded == false) + return false; + + succeeded = false; + if (initiate && (gssICred == null)) { + gssName = null; + gssICred = null; + gssACred = null; + throw new LoginException("Null Initiator Credential"); + } + if (accept && (gssACred == null)) { + gssName = null; + gssICred = null; + gssACred = null; + throw new LoginException("Null Acceptor Credential"); + } + if (subject.isReadOnly()) { + gssName = null; + gssICred = null; + gssACred = null; + throw new LoginException("Subject is Readonly"); + } + + try { + if (initiate && gssName == null) + gssName = gssICred.getName(); + } catch (GSSException e) {} + try { + if (accept && gssName == null) + gssName = gssACred.getName(); + } catch (GSSException e) {} + + Set privCredSet = subject.getPrivateCredentials(); + Set princSet = subject.getPrincipals(); + + if (gssName != null && !princSet.contains(gssName)) + princSet.add(gssName); + if (gssICred != null && !privCredSet.contains(gssICred)) + privCredSet.add(gssICred); + if (gssACred != null && !privCredSet.contains(gssACred)) + privCredSet.add(gssACred); + + succeeded = true; + commitSucceeded = true; + if (debug) + System.out.println("\t\t[GssLoginModule] commit Succeeded"); + return true; + } + + /** + *

This method is called if the LoginContext's + * overall authentication failed. + * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL + * LoginModules did not succeed).

+ * + *

If this LoginModule's own authentication attempt + * succeeded (checked by retrieving the private state saved by the + * login and commit methods), + * then this method cleans up any state that was originally + * saved.

+ * + *

+ * + * @exception LoginException if the abort fails.

+ * + *

+ * @return false if this LoginModule's own login and/or commit attempts + * failed, and true otherwise.

+ */ + + public boolean abort() throws LoginException { + if (succeeded == false) { + return false; + } else if (succeeded == true && commitSucceeded == false) { + // login succeeded but overall authentication failed + succeeded = false; + } else { + // overall authentication succeeded and commit succeeded, + // but someone else's commit failed + logout(); + } + return true; + } + + /** + *

Logout the user.

+ * + *

This method removes the GSSName and + * GSSCredential added by the commit method.

+ * + *

+ * + * @exception LoginException if the logout fails.

+ * + *

+ * @return true in all cases since this LoginModule + * should not be ignored.

+ */ + public boolean logout() throws LoginException { + if (subject.isReadOnly()) + throw new LoginException("Subject is Readonly"); + + subject.getPrincipals().remove(gssName); + Iterator it = subject.getPrivateCredentials().iterator(); + while (it.hasNext()) { + Object o = it.next(); + if (o instanceof GSSCredential) + it.remove(); + } + + succeeded = false; + commitSucceeded = false; + if (debug) + System.out.println("\t\t[GSSLoginModule]: logged out Subject"); + return true; + } + + /** + * Clean out the state + */ + private void cleanState() { + + // save input as shared state only if + // authentication succeeded + if (succeeded) { + if (storePass && + !sharedState.containsKey(NAME) && + !sharedState.containsKey(PWD)) { + sharedState.put(NAME, name); + sharedState.put(PWD, password); + } + } else { + // remove temp results for the next try + gssName = null; + gssICred = null; + gssACred = null; + } + name = null; + password = null; + if (clearPass) { + sharedState.remove(NAME); + sharedState.remove(PWD); + } + } +} diff --git a/test/jdk/sun/security/jgss/DefaultGssConfig.java b/test/jdk/sun/security/jgss/DefaultGssConfig.java index e80a2aaf365..de8bf0b8697 100644 --- a/test/jdk/sun/security/jgss/DefaultGssConfig.java +++ b/test/jdk/sun/security/jgss/DefaultGssConfig.java @@ -58,6 +58,10 @@ public static void main(String[] argv) throws Exception { Configuration.getConfiguration(); // 3. Make sure there're default entries for GSS krb5 client/server + // + // FIXME Why be so Kerberos-specific? This is wrong. Instead we could + // use a command-line argument to deal with a specific (or all) + // mechanisms. LoginConfigImpl lc = new LoginConfigImpl(GSSCaller.CALLER_INITIATE, GSSUtil.GSS_KRB5_MECH_OID); if (lc.getAppConfigurationEntry("").length == 0) { throw new Exception("No default config for GSS krb5 client"); From 98f952fe4fad45d3b02cf59b17375086ef43865b Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Fri, 11 Mar 2016 20:54:00 +0000 Subject: [PATCH 18/25] Engage GssLoginModule (only) when native=true Also don't force same name for acceptor and initiator. --- .../sun/security/jgss/LoginConfigImpl.java | 55 +++++++++++++------ .../security/auth/module/GssLoginModule.java | 53 +++++++++++++++++- 2 files changed, 88 insertions(+), 20 deletions(-) diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java b/src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java index 4d543044cc4..951f573d544 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java @@ -42,6 +42,7 @@ public class LoginConfigImpl extends Configuration { private final Configuration config; private final GSSCaller caller; private final String mechName; + private final boolean useNative; private static final sun.security.util.Debug debug = sun.security.util.Debug.getInstance("gssloginconfig", "\t[GSS LoginConfigImpl]"); @@ -65,8 +66,17 @@ public LoginConfigImpl(GSSCaller caller, Oid mech) { this.caller = caller; - if (mech.equals(GSSUtil.GSS_KRB5_MECH_OID)) { + useNative = "true".equalsIgnoreCase( + System.getProperty("sun.security.jgss.native")); + + if (mech.equals(GSSUtil.GSS_KRB5_MECH_OID) || + mech.equals(GSSUtil.GSS_KRB5_MECH_OID2) || + mech.equals(GSSUtil.GSS_KRB5_MECH_OID_MS)) { mechName = "krb5"; + } else if (useNative) { + // We don't really need a mechName, nor do we have any sort of + // standard notion of mechanism name (other than OIDs). + mechName = mech.toString(); } else { throw new IllegalArgumentException(mech.toString() + " not supported"); } @@ -98,7 +108,7 @@ public AppConfigurationEntry[] getAppConfigurationEntry(String name) { // For the 4 old callers, old entry names will be used if the new // entry name is not provided. - if ("krb5".equals(mechName)) { + if ("krb5".equals(mechName) || useNative) { if (caller == GSSCaller.CALLER_INITIATE) { alts = new String[] { "com.sun.security.jgss.krb5.initiate", @@ -118,7 +128,7 @@ public AppConfigurationEntry[] getAppConfigurationEntry(String name) { } } else { throw new IllegalArgumentException(mechName + " not supported"); - // No other mech at the moment, maybe -- + // No other Java-coded mech at the moment, maybe -- /* switch (caller) { case GSSUtil.CALLER_INITIATE: @@ -165,33 +175,42 @@ public AppConfigurationEntry[] getAppConfigurationEntry(String name) { * the system-wide Configuration object. */ private AppConfigurationEntry[] getDefaultConfigurationEntry() { - HashMap options = new HashMap (2); + HashMap gssOptions = new HashMap (2); + HashMap krb5Options = new HashMap (2); - if (mechName == null || mechName.equals("krb5")) { + if (mechName == null || mechName.equals("krb5") || useNative) { if (isServerSide(caller)) { + gssOptions.put("useDefaultCreds", "true"); + gssOptions.put("doNotPrompt", "true"); + gssOptions.put("accept", "true"); // 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"); + krb5Options.put("useKeyTab", "true"); + krb5Options.put("storeKey", "true"); + krb5Options.put("doNotPrompt", "true"); + krb5Options.put("principal", "*"); + krb5Options.put("isInitiator", "false"); } else { if (caller instanceof HttpCaller && !HTTP_USE_GLOBAL_CREDS) { - options.put("useTicketCache", "false"); + gssOptions.put("tryDefaultCreds", "false"); + krb5Options.put("useTicketCache", "false"); } else { - options.put("useTicketCache", "true"); + gssOptions.put("tryDefaultCreds", "true"); + krb5Options.put("useTicketCache", "true"); } - options.put("doNotPrompt", "false"); + gssOptions.put("initiate", "true"); + gssOptions.put("doNotPrompt", "false"); + krb5Options.put("doNotPrompt", "false"); } return new AppConfigurationEntry[] { - // FIXME Also -or alternatively when we're using the native - // providers- add GssLoginModule, since we might not be able to - // acquire Kerberos credentials directly, or even at all . new AppConfigurationEntry( - "com.sun.security.auth.module.Krb5LoginModule", + "com.sun.security.auth.module.GssLoginModule", AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, - options) + gssOptions), + new AppConfigurationEntry( + "com.sun.security.auth.module.Krb5LoginModule", + AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT, + krb5Options) }; } return null; diff --git a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/GssLoginModule.java b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/GssLoginModule.java index b3253b679df..2e81f7fbfc4 100644 --- a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/GssLoginModule.java +++ b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/GssLoginModule.java @@ -68,6 +68,8 @@ public class GssLoginModule implements LoginModule { private GSSCredential gssICred; private GSSCredential gssACred; + private boolean useNative; // sun.security.jgss.native property + private boolean useFirstPass; private boolean tryFirstPass; private boolean storePass; @@ -125,6 +127,34 @@ public void initialize(Subject subject, this.sharedState = (Map)sharedState; this.options = options; + /* + * When sun.security.jgss.native=false (i.e., not using the system's + * native C/ELF/DLL GSS implementation) then there's nothing for this + * login module to do. Otherwise we'd get into an infinite recursion + * problem due to re-entering GssLoginModule like this: + * + * Application -> LoginContext -> GssLoginModule -> Krb5 -> + * GSSUtil.login -> LoginContext -> GssLoginModule -> ... + * + * It stands to reason that when sun.security.jgss.native=false the + * login modules corresponding to the actual GSS mechanisms coded in + * Java are the ones that should be acquiring their corresponding + * credentials. + * + * A policy like "let the application use GSS credentials but not the + * raw, underlying Krb5 credentials" when + * sun.security.jgss.native=false" could be expressible by adding a + * module option to Krb5LoginModule that causes it to add only GSS + * credentials to the Subject, not Krb5 credentials. + * + * (It has never been possible to express such a policy, so we lose + * nothing by punting here when sun.security.jgss.native=false.) + */ + useNative = "true".equalsIgnoreCase( + System.getProperty("sun.security.jgss.native")); + if (!useNative) + return; + manager = GSSManager.getInstance(); // initialize any configured options @@ -212,6 +242,13 @@ public void initialize(Subject subject, */ public boolean login() throws LoginException { succeeded = false; + + /* + * See commentary in initialize(). By returning false we cause + * LoginContext to ignore this module. + */ + if (!useNative) + return false; try { if (tryFirstPass || useFirstPass) { attemptAuthentication(true); @@ -265,8 +302,6 @@ private void getcreds() throws GSSException { gssICred = manager.createCredential(gssName, password, GSSCredential.DEFAULT_LIFETIME, (Oid[])null, GSSCredential.INITIATE_ONLY); - if (gssName == null) - gssName = gssICred.getName(); if (debug) System.out.println("\t\t[GssLoginModule] acquired" + " initiator credentials: " + gssName); @@ -284,6 +319,10 @@ private void getcreds() throws GSSException { System.out.println("\t\t[GssLoginModule] acquired" + " acceptor credentials"); } + if (gssName == null && gssICred != null) + gssName = gssICred.getName(); + if (gssName == null && gssACred != null) + gssName = gssACred.getName(); } private void attemptAuthentication(boolean getPasswdFromSharedState) @@ -463,6 +502,9 @@ public boolean commit() throws LoginException { if (succeeded == false) return false; + if (!useNative) + return false; + succeeded = false; if (initiate && (gssICred == null)) { gssName = null; @@ -559,6 +601,13 @@ public boolean abort() throws LoginException { * should not be ignored.

*/ public boolean logout() throws LoginException { + /* + * See commentary in initialize(). By returning false we cause + * LoginContext to ignore this module. + */ + if (!useNative) + return false; + if (subject.isReadOnly()) throw new LoginException("Subject is Readonly"); From 78a7cac618cd563d1e9063184fb9ecbd72576975 Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Tue, 8 Mar 2016 23:54:45 +0000 Subject: [PATCH 19/25] Krb5LoginModule cleanup Add commentary about native in Krb5LoginModule --- .../sun/security/krb5/KrbAsReqBuilder.java | 3 + .../security/auth/module/Krb5LoginModule.java | 434 +++++++++--------- 2 files changed, 231 insertions(+), 206 deletions(-) diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java b/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java index 953bb557041..cb16286ba15 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java @@ -397,6 +397,9 @@ public void destroy() { } } + // XXX It'd be nice to implement AutoCloseable with a close() that calls + // destroy() + /** * Checks if the current state is the specified one. * @param st the expected state diff --git a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java index 62138dee284..97a951f28f4 100644 --- a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java +++ b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java @@ -27,6 +27,7 @@ package com.sun.security.auth.module; import java.io.*; +import java.security.Principal; import java.text.MessageFormat; import java.util.*; @@ -409,7 +410,6 @@ public class Krb5LoginModule implements LoginModule { private Credentials cred = null; private PrincipalName principal = null; - private KerberosPrincipal kerbClientPrinc = null; private KerberosTicket kerbTicket = null; private KerberosKey[] kerbKeys = null; private StringBuffer krb5PrincName = null; @@ -520,6 +520,16 @@ public void initialize(Subject subject, */ public boolean login() throws LoginException { + /* + * Perhaps we should wrap this in a method that returns false if this + * throws and sun.security.jgss.native=true. Or perhaps the wrapper + * could see if it can acquire comparable GSS credentials and then + * store those in the subject in commit() in that case (and then + * GSSUtil/Krb5Util code could be changed to look for those). + * + * See related commentary in GssLoginModule. + */ + if (refreshKrb5Config) { try { if (debug) { @@ -532,18 +542,30 @@ public boolean login() throws LoginException { throw le; } } + + // -Dsun.security.krb5.principal takes precedence over login module + // "principal" option + // + // XXX This seems misplaced. This is configuration reading, and that + // clearly belongs in initialize(). It's not like it's very likely + // that this sequence of events takes place anywhere, much less that we + // should cater to it: + // + // lc.initialize(); + // System.setProperty("sun.security.krb5.principal", ...); + // lc.login(); String principalProperty = System.getProperty ("sun.security.krb5.principal"); if (principalProperty != null) { krb5PrincName = new StringBuffer(principalProperty); - } else { - if (princName != null) { - krb5PrincName = new StringBuffer(princName); - } + } else if (princName != null) { + krb5PrincName = new StringBuffer(princName); } + // XXX This really belongs in initialize() validateConfiguration(); + // XXX This really belongs in validateConfiguration() if (krb5PrincName != null && krb5PrincName.toString().equals("*")) { unboundServer = true; } @@ -632,51 +654,31 @@ private void attemptAuthentication(boolean getPasswdFromSharedState) } try { + // This means "from the traditional FILE ccache" if (useTicketCache) { - // ticketCacheName == null implies the default cache if (debug) - System.out.println("Acquire TGT from Cache"); - cred = Credentials.acquireTGTFromCache - (principal, ticketCacheName); - + System.out.println("Trying to acquire TGT from Cache"); + cred = getCredsFromCCache(principal, renewTGT, ticketCacheName); if (cred != null) { - if (renewTGT && isOld(cred)) { - // renew if ticket is old. - Credentials newCred = renewCredentials(cred); - if (newCred != null) { - cred = newCred; - } - } + if (principal == null) + principal = cred.getClient(); if (!isCurrent(cred)) { - // credentials have expired cred = null; if (debug) - System.out.println("Credentials are" + - " no longer valid"); - } - } - - if (cred != null) { - // get the principal name from the ticket cache - if (principal == null) { - principal = cred.getClient(); - } - } - if (debug) { - System.out.println("Principal is " + principal); - if (cred == null) { - System.out.println - ("null credentials from Ticket Cache"); + System.out.println("Found expired cached " + + "credentials for " + principal); + } else if (debug) { + System.out.println("Found cached credentials for " + + principal); } + } else if (debug) { + System.out.println("Could not find cached credentials"); } } - // cred = null indicates that we didn't get the creds - // from the cache or useTicketCache was false - if (cred == null) { - // We need the principal name whether we use keytab - // or AS Exchange + // !useTicketCache || credentials not found || expired + if (principal == null) { promptForName(getPasswdFromSharedState); principal = new PrincipalName @@ -684,49 +686,19 @@ private void attemptAuthentication(boolean getPasswdFromSharedState) PrincipalName.KRB_NT_PRINCIPAL); } - /* - * Before dynamic KeyTab support (6894072), here we check if - * the keytab contains keys for the principal. If no, keytab - * will not be used and password is prompted for. - * - * After 6894072, we normally don't check it, and expect the - * keys can be populated until a real connection is made. The - * check is still done when isInitiator == true, where the keys - * will be used right now. - * - * Probably tricky relations: - * - * useKeyTab is config flag, but when it's true but the ktab - * does not contains keys for principal, we would use password - * and keep the flag unchanged (for reuse?). In this method, - * we use (ktab != null) to check whether keytab is used. - * After this method (and when storeKey == true), we use - * (encKeys == null) to check. - */ if (useKeyTab) { - if (!unboundServer) { - KerberosPrincipal kp = - new KerberosPrincipal(principal.getName()); - ktab = (keyTabName == null) - ? KeyTab.getInstance(kp) - : KeyTab.getInstance(kp, new File(keyTabName)); - } else { - ktab = (keyTabName == null) - ? KeyTab.getUnboundInstance() - : KeyTab.getUnboundInstance(new File(keyTabName)); - } - if (isInitiator) { - if (Krb5Util.keysFromJavaxKeyTab(ktab, principal).length + ktab = getKtab(keyTabName, principal, unboundServer); + if (isInitiator && + Krb5Util.keysFromJavaxKeyTab(ktab, principal).length == 0) { - ktab = null; - if (debug) { - System.out.println - ("Key for the principal " + - principal + - " not available in " + - ((keyTabName == null) ? - "default key tab" : keyTabName)); - } + ktab = null; + if (debug) { + System.out.println + ("Key for the principal " + + principal + + " not available in " + + ((keyTabName == null) ? + "default key tab" : keyTabName)); } } } @@ -736,16 +708,21 @@ private void attemptAuthentication(boolean getPasswdFromSharedState) if (ktab == null) { promptForPass(getPasswdFromSharedState); builder = new KrbAsReqBuilder(principal, password); - if (isInitiator) { - // XXX Even if isInitiator=false, it might be - // better to do an AS-REQ so that keys can be - // updated with PA info + if (isInitiator || storeKey) { + // Even if isInitiator=false, if we want to accept with + // long-term key derived from the password, then in + // principle (and decidedly for new enctypes) we need + // to do an AS exchange to get the PA etype info for + // the derivation. (For older enctypes this is bad, as + // we will attempt to talk the a KDC we might not be + // able to reach, then timeout... If this is not + // desired, the user can reconfigure the module.) cred = builder.action().getCreds(); - } - if (storeKey) { - encKeys = builder.getKeys(isInitiator); - // When encKeys is empty, the login actually fails. - // For compatibility, exception is thrown in commit(). + if (storeKey) { + encKeys = builder.getKeys(isInitiator); + // When encKeys is empty, the login actually fails. + // For compatibility, exception is thrown in commit(). + } } } else { builder = new KrbAsReqBuilder(principal, ktab); @@ -762,15 +739,15 @@ private void attemptAuthentication(boolean getPasswdFromSharedState) System.out.println("Will use keytab"); } else if (storeKey) { for (int i = 0; i < encKeys.length; i++) { + // Printing keys here just because debug is a bad + // idea: stdout might be a file that gets sent to + // loggers, and... yeah, no. System.out.println("EncryptionKey: keyType=" + - encKeys[i].getEType() + - " keyBytes (hex dump)=" + - hd.encodeBuffer(encKeys[i].getBytes())); + encKeys[i].getEType()); } } } - // we should hava a non-null cred if (isInitiator && (cred == null)) { throw new LoginException ("TGT Can not be obtained from the KDC "); @@ -961,6 +938,29 @@ private void validateConfiguration() throws LoginException { } } + private Credentials getCredsFromCCache(PrincipalName princ, boolean renewTGT, String ccacheName) + throws KrbException, IOException { + // ticketCacheName == null implies the default cache + // princ == null implies the cache's default princ(XXX?) + Credentials creds = Credentials.acquireTGTFromCache(princ, ccacheName); + if (creds == null) + return null; + if (renewTGT && timeToRenew(creds)) + creds = possiblyRenewCreds(creds); + // It's the caller's job to deal with expired creds + return creds; + } + + private KeyTab getKtab(String keyTabName, PrincipalName principal, + boolean unboundServer) + { + KerberosPrincipal kp = unboundServer ? null : + new KerberosPrincipal(principal.getName());; + return (keyTabName == null) + ? KeyTab.getInstance(kp) // default keytab + : KeyTab.getInstance(kp, new File(keyTabName)); + } + private static boolean isCurrent(Credentials creds) { Date endTime = creds.getEndTime(); @@ -970,47 +970,60 @@ private static boolean isCurrent(Credentials creds) return true; } - private static boolean isOld(Credentials creds) + private static boolean timeToRenew(Credentials creds) { + if (!creds.isRenewable()) + return false; + Date endTime = creds.getEndTime(); - if (endTime != null) { - Date authTime = creds.getAuthTime(); - long now = System.currentTimeMillis(); - if (authTime != null) { - // pass the mid between auth and end - return now - authTime.getTime() > endTime.getTime() - now; - } else { - // will expire in less than 2 hours - return now <= endTime.getTime() - 1000*3600*2L; - } - } - return false; + + // endtime is required, so it can't be null. We only have to check + // because it's Java and we could express that this can't be null. + // Strictly speaking we can leave out this test. + if (endTime == null) + return false; + + // There's no point trying to renew a TGT we will be able to renew but + // with no additional lifetime. And there's no point trying to renew + // non-renewable tickets. + Date renewTill = creds.getRenewTill(); + if (renewTill == null || renewTill.getTime() <= endTime.getTime()) + return false; + + // NOTE WELL: We must use the *start* time, not the auth time, because + // the auth time refers to when the AS exchange was done, + // not to when the TGS exchange was done. For very + // long-lived TGTs using authTime here means renewing all + // the time! + Date startTime = creds.getStartTime(); + long now = System.currentTimeMillis(); + // Start time can be null + if (startTime != null) + // past the mid between start and end + return now - startTime.getTime() > endTime.getTime() - now; + // will it expire in less than 2 hours? + return now <= endTime.getTime() - 1000*3600*2L; } - private Credentials renewCredentials(Credentials creds) + private Credentials possiblyRenewCreds(Credentials creds) + throws KrbException, IOException { - Credentials lcreds; + if (!creds.isRenewable()) + return creds; + + if (System.currentTimeMillis() > cred.getRenewTill().getTime()) + return creds; + try { - if (!creds.isRenewable()) - throw new RefreshFailedException("This ticket" + - " is not renewable"); - if (creds.getRenewTill() == null) { - // Renewable ticket without renew-till. Illegal and ignored. - return creds; - } - if (System.currentTimeMillis() > cred.getRenewTill().getTime()) - throw new RefreshFailedException("This ticket is past " - + "its last renewal time."); - lcreds = creds.renew(); + creds = creds.renew(); if (debug) System.out.println("Renewed Kerberos Ticket"); } catch (Exception e) { - lcreds = null; if (debug) System.out.println("Ticket could not be renewed : " + e.getMessage()); } - return lcreds; + return creds; } /** @@ -1036,107 +1049,108 @@ private Credentials renewCredentials(Credentials creds) */ public boolean commit() throws LoginException { - /* * Let us add the Krb5 Creds to the Subject's * private credentials. The credentials are of type * KerberosKey or KerberosTicket */ if (succeeded == false) { + cleanKerberosCred(); return false; - } else { + } - if (isInitiator && (cred == null)) { - succeeded = false; - throw new LoginException("Null Client Credential"); - } + if (isInitiator && (cred == null)) { + cleanKerberosCred(); + succeeded = false; + throw new LoginException("Null Client Credential"); + } - if (subject.isReadOnly()) { - cleanKerberosCred(); - throw new LoginException("Subject is Readonly"); - } + if (subject.isReadOnly()) { + cleanKerberosCred(); + succeeded = false; + throw new LoginException("Subject is Readonly"); + } - /* - * Add the Principal (authenticated identity) - * to the Subject's principal set and - * add the credentials (TGT or Service key) to the - * Subject's private credentials - */ + try { + setupSubject(subject, unboundServer ? null : principal, ktab, + isInitiator ? Krb5Util.credsToTicket(cred) : null, + storeKey && encKeys != null ? encKeys : null); + if (debug) + System.out.println("Added Kerberos credentials to subject"); + return true; + } catch (Exception e) { + cleanKerberosCred(); + succeeded = false; + throw new LoginException(e.getMessage()); + } + } - Set privCredSet = subject.getPrivateCredentials(); - Set princSet = subject.getPrincipals(); - kerbClientPrinc = new KerberosPrincipal(principal.getName()); + /** + * Store the given Kerberos crendentials in the given subject. + * + * @param subject the {@code Subject} to store the credentials into + * + * @param principal the {@code PrincipalName} for the credentials; use null to refer to all principals in the keytab + * + * @param ktab a {@code KeyTab} keytab to use for acting as a service (may be null) + * + * @param kerbTicket the TGT for the principal (if acting as a client) + * + * @param encKeys long-term secret keys for the principal (if acting as a server with the keys derived from a password) + * + */ + private static void setupSubject(Subject subject, PrincipalName principal, + KeyTab ktab, KerberosTicket kerbTicket, EncryptionKey[] encKeys) + throws LoginException { - // create Kerberos Ticket - if (isInitiator) { - kerbTicket = Krb5Util.credsToTicket(cred); - } + /* + * Add the Principal (authenticated identity) + * to the Subject's principal set and + * add the credentials (TGT or Service key) to the + * Subject's private credentials + */ - if (storeKey && encKeys != null) { - if (encKeys.length == 0) { - succeeded = false; - throw new LoginException("Null Server Key "); - } + if (kerbTicket == null && encKeys == null && ktab == null) + throw new LoginException("No Kerberos credentials provided to " + + "store in subject"); - kerbKeys = new KerberosKey[encKeys.length]; - for (int i = 0; i < encKeys.length; i ++) { - Integer temp = encKeys[i].getKeyVersionNumber(); - kerbKeys[i] = new KerberosKey(kerbClientPrinc, - encKeys[i].getBytes(), - encKeys[i].getEType(), - (temp == null? - 0: temp.intValue())); - } + Set privCredSet = subject.getPrivateCredentials(); + Set princSet = subject.getPrincipals(); - } - // Let us add the kerbClientPrinc,kerbTicket and KeyTab/KerbKey (if - // storeKey is true) + KerberosPrincipal princ = null; + if (principal != null) { + princ = new KerberosPrincipal(principal.getName()); + if (!princSet.contains(princ)) + princSet.add(princ); + } - // We won't add "*" as a KerberosPrincipal - if (!unboundServer && - !princSet.contains(kerbClientPrinc)) { - princSet.add(kerbClientPrinc); - } + if (kerbTicket != null && !privCredSet.contains(kerbTicket)) + privCredSet.add(kerbTicket); - // add the TGT - if (kerbTicket != null) { - if (!privCredSet.contains(kerbTicket)) - privCredSet.add(kerbTicket); - } + if (ktab != null && !privCredSet.contains(ktab)) + privCredSet.add(ktab); - if (storeKey) { - if (encKeys == null) { - if (ktab != null) { - if (!privCredSet.contains(ktab)) { - privCredSet.add(ktab); - } - } else { - succeeded = false; - throw new LoginException("No key to store"); - } - } else { - for (int i = 0; i < kerbKeys.length; i ++) { - if (!privCredSet.contains(kerbKeys[i])) { - privCredSet.add(kerbKeys[i]); - } - encKeys[i].destroy(); - encKeys[i] = null; - if (debug) { - System.out.println("Added server's key" - + kerbKeys[i]); - System.out.println("\t\t[Krb5LoginModule] " + - "added Krb5Principal " + - kerbClientPrinc.toString() - + " to Subject"); - } - } - } - } + if (encKeys == null) + return; + + if (encKeys.length == 0) + throw new LoginException("Cannot store empty long-term " + + "keyset in Subject"); + + if (princ == null) + throw new LoginException("Cannot store Kerberos long-term keys " + + "for wild-card principal in Subject"); + + for (int i = 0; i < encKeys.length; i ++) { + Integer temp = encKeys[i].getKeyVersionNumber(); + KerberosKey kerbKey = new KerberosKey(princ, + encKeys[i].getBytes(), + encKeys[i].getEType(), + (temp == null? + 0: temp.intValue())); + if (!privCredSet.contains(kerbKey)) + privCredSet.add(kerbKey); } - commitSucceeded = true; - if (debug) - System.out.println("Commit Succeeded \n"); - return true; } /** @@ -1194,8 +1208,13 @@ public boolean logout() throws LoginException { throw new LoginException("Subject is Readonly"); } - subject.getPrincipals().remove(kerbClientPrinc); - // Let us remove all Kerberos credentials stored in the Subject + Iterator itp = subject.getPrincipals().iterator(); + while (itp.hasNext()) { + Object o = itp.next(); + if (o instanceof KerberosPrincipal) + itp.remove(); + } + Iterator it = subject.getPrivateCredentials().iterator(); while (it.hasNext()) { Object o = it.next(); @@ -1234,9 +1253,12 @@ private void cleanKerberosCred() throws LoginException { throw new LoginException ("Destroy Failed on Kerberos Private Credentials"); } + for (int i = 0; i < kerbKeys.length; i++) { + encKeys[i].destroy(); + encKeys[i] = null; + } kerbTicket = null; kerbKeys = null; - kerbClientPrinc = null; } /** From 5a494b0b21328fe86cf4e31c4c49a642b1b01912 Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Tue, 14 Aug 2018 15:50:26 -0500 Subject: [PATCH 20/25] fixup SEAM bug uncomments --- .../share/native/libj2gss/GSSLibStub.c | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c b/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c index c5ba7368213..0e72bdf250a 100644 --- a/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c +++ b/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c @@ -1000,9 +1000,11 @@ Java_sun_security_jgss_wrapper_GSSLibStub_initContext(JNIEnv *env, FID_NativeGSSContext_isEstablished, JNI_TRUE); - jMech = getJavaOID(env, aMech); - (*env)->SetObjectField(env, jcontextSpi, - FID_NativeGSSContext_actualMech, jMech); + jMech = (aMech == GSS_C_NO_OID) ? NULL : getJavaOID(env, aMech); + if (!(*env)->ExceptionCheck(env)) { + (*env)->SetObjectField(env, jcontextSpi, + FID_NativeGSSContext_actualMech, jMech); + } } else if (major & GSS_S_CONTINUE_NEEDED) { TRACE0("[GSSLibStub_initContext] context not established"); major &= ~GSS_S_CONTINUE_NEEDED; @@ -1103,16 +1105,13 @@ Java_sun_security_jgss_wrapper_GSSLibStub_acceptContext(JNIEnv *env, if (GSS_ERROR(major) == GSS_S_COMPLETE) { /* update member values if needed */ - if (aMech != GSS_C_NO_OID) { - jMech = getJavaOID(env, aMech); - if ((*env)->ExceptionCheck(env)) { - goto error; - } + jMech = (aMech == GSS_C_NO_OID) ? NULL : getJavaOID(env, aMech); + if (!(*env)->ExceptionCheck(env)) { (*env)->SetObjectField(env, jcontextSpi, FID_NativeGSSContext_actualMech, jMech); - if ((*env)->ExceptionCheck(env)) { - goto error; - } + } + if ((*env)->ExceptionCheck(env)) { + goto error; } /* WORKAROUND for an old Heimdal bug */ From ac275243c66d56cd0cd0ebd5c8ecb1bc0e7b1ce8 Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Tue, 14 Aug 2018 15:50:48 -0500 Subject: [PATCH 21/25] Fix leak in exception cases in getJavaOID() --- .../share/native/libj2gss/NativeUtil.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/java.security.jgss/share/native/libj2gss/NativeUtil.c b/src/java.security.jgss/share/native/libj2gss/NativeUtil.c index fab2c007bb4..a9e2cf507be 100644 --- a/src/java.security.jgss/share/native/libj2gss/NativeUtil.c +++ b/src/java.security.jgss/share/native/libj2gss/NativeUtil.c @@ -786,17 +786,14 @@ jobject getJavaOID(JNIEnv *env, gss_OID cOid) { if (jbytes == NULL) { return NULL; } - (*env)->SetByteArrayRegion(env, jbytes, 0, 2, (jbyte *) oidHdr); - if ((*env)->ExceptionCheck(env)) { - return NULL; + if (!(*env)->ExceptionCheck(env)) { + (*env)->SetByteArrayRegion(env, jbytes, 0, 2, (jbyte *) oidHdr); } - (*env)->SetByteArrayRegion(env, jbytes, 2, cLen, (jbyte *) cOid->elements); - if ((*env)->ExceptionCheck(env)) { - return NULL; + if (!(*env)->ExceptionCheck(env)) { + (*env)->SetByteArrayRegion(env, jbytes, 2, cLen, (jbyte *) cOid->elements); } - result = (*env)->NewObject(env, CLS_Oid, MID_Oid_ctor1, jbytes); - if ((*env)->ExceptionCheck(env)) { - return NULL; + if (!(*env)->ExceptionCheck(env)) { + result = (*env)->NewObject(env, CLS_Oid, MID_Oid_ctor1, jbytes); } (*env)->DeleteLocalRef(env, jbytes); return result; From 90d3097af077cd75cc624c874bac3df8f6ba1543 Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Fri, 4 Dec 2015 22:14:59 -0500 Subject: [PATCH 22/25] FIXME commentary --- .../classes/sun/security/jgss/spnego/SpNegoMechFactory.java | 4 ++++ .../classes/sun/security/jgss/wrapper/GSSCredElement.java | 4 ++++ .../sun/security/jgss/wrapper/SunNativeProvider.java | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoMechFactory.java b/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoMechFactory.java index 5ae19bed343..336f88e8237 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoMechFactory.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/spnego/SpNegoMechFactory.java @@ -79,6 +79,10 @@ private static SpNegoCredElement getCredFromSubject(GSSNameSpi name, null : creds.firstElement()); // Force permission check before returning the cred to caller + // + // FIXME This code assumes that the Kerberos mechanism is Java-coded, + // whereas it should be possible to mix Java-coded SPNEGO with a native + // (C-coded) mechanism. For now this assumption stands. if (result != null) { GSSCredentialSpi cred = result.getInternalCred(); if (GSSUtil.isKerberosMech(cred.getMechanism())) { diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java index 5d2b7e6486d..c2fc3152684 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSCredElement.java @@ -45,7 +45,11 @@ public class GSSCredElement implements GSSCredentialSpi { public boolean isDefCred; // Perform the necessary ServicePermission check on this cred + // FIXME Don't use any Krb5-specific code here. void doServicePermCheck() throws GSSException { + // FIXME We need only do this check in initSecContext() and + // acceptSecContext(), so gut this here, and never ever do the + // Krb5Util.getTGSName(name) check. if (GSSUtil.isKerberosMech(cStub.getMech())) { if (System.getSecurityManager() != null) { if (isInitiatorCredential()) { diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java index 25ee2a693d0..e5b6edf2a76 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java @@ -105,6 +105,12 @@ public HashMap run() { Oid[] mechs = GSSLibStub.indicateMechs(); HashMap map = new HashMap(); + // If the GSSLibStub does not support SPNEGO, + // we could use ours, but ours has too much + // knowledge of the Java Krb5 GSS mechanism, so + // we can't, but if we could we'd do it thusly: + // map.put("GssApiMechanism.1.3.6.1.5.5.2", + // "sun.security.jgss.spnego.SpNegoMechFactory"); for (int i = 0; i < mechs.length; i++) { debug("Native MF for " + mechs[i]); map.put("GssApiMechanism." + mechs[i], From 5a541ee4003960cf3fe375544a79ce9925d43312 Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Thu, 27 Sep 2018 12:41:48 -0500 Subject: [PATCH 23/25] Add commentary about permissions checks --- .../security/jgss/wrapper/GSSNameElement.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java index d066de5e961..2027f58377b 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/GSSNameElement.java @@ -198,6 +198,27 @@ public String getKrbName() throws GSSException { long mName = 0; GSSLibStub stub = cStub; if (!GSSUtil.isKerberosMech(mech)) { + // XXX We can't expect this to work generally. We should + // generalize the permission checks so that they can deal + // with name forms other than those of Kerberos. + // + // Alternatively we could have a method in GSSLibStub for + // mapping a non-Kerberos MN to a Kerberos MN, but depending + // on the specifics of the non-Kerberos mechanism we would + // either end up needing new conventions for Kerberos naming + // or else having cases where we can end up failing to + // support unconventional name forms. + // + // Consider a SAML assertion with a variety of identifying + // attributes and a variety of non-identifying attributes + // that are relevant to authorization. How should a + // Kerberos-equivalent be constructed? GSS does have + // extensions for decorating name objects with attributes, + // so that's not an issue, but if there are multiple + // identifying attributes then we'd have to pick one. Now, + // suppose the identifying attribute has a form like + // -- we'd need a Kerberos convention for + // that, which might be PHONE/@. stub = GSSLibStub.getInstance(GSSUtil.GSS_KRB5_MECH_OID); } mName = stub.canonicalizeName(pName); From 192cc47232535b38db775618610fccc1524ade64 Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Wed, 26 Sep 2018 18:25:37 -0500 Subject: [PATCH 24/25] JGSS: Simplify context permissions checks We were reacquiring the initiator/acceptor credential upon security context full establishment in order to indirectly perform a permission check on the srcName/targName once we find out what they are. But this is just one more way to end up failing, which happens with Heimdal when using SPNEGO because we ask to acquire a Kerberos credentials using a SPNEGO MN and that fails. Also, there was a security bug here: if the permission check fails then we raise, but if the application already has a context handle, then it can use it anyways if it catches the exception! The fix for this is to dispose() when the permission check fails. --- .../jgss/wrapper/NativeGSSContext.java | 74 +++++++------------ 1 file changed, 26 insertions(+), 48 deletions(-) diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java index 440f30c7a4f..40cebf8e7c4 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java @@ -74,8 +74,8 @@ class NativeGSSContext implements GSSContextSpi { private int lifetime = GSSCredential.DEFAULT_LIFETIME; private final GSSLibStub cStub; - private boolean skipDelegPermCheck; - private boolean skipServicePermCheck; + private boolean skipDelegPermCheck = false; + private boolean skipServicePermCheck = false; // Retrieve the (preferred) mech out of SPNEGO tokens, i.e. // NegTokenInit & NegTokenTarg @@ -110,28 +110,20 @@ private static Oid getMechFromSpNegoToken(byte[] token, // 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 { + try { + if (isInitiator && srcName != null) { String tgsName = Krb5Util.getTGSName(srcName); - Krb5Util.checkServicePermission(tgsName, action); + Krb5Util.checkServicePermission(tgsName, "initiate"); + skipServicePermCheck = true; + } else if (!isInitiator && targetName != null) { + String targetStr = targetName.getKrbName(); + Krb5Util.checkServicePermission(targetStr, "accept"); + skipServicePermCheck = true; } + } catch (GSSException ge) { + dispose(); + throw ge; } - if (targetName == null) - return; - String targetStr = targetName.getKrbName(); - Krb5Util.checkServicePermission(targetStr, action); - skipServicePermCheck = true; } } @@ -203,13 +195,13 @@ private byte[] retrieveToken(InputStream is, int mechTokenLen) lifetime = time; if (GSSUtil.isKerberosMech(cStub.getMech())) { - doServicePermCheck(); if (cred == null) { disposeCred = cred = new GSSCredElement(null, lifetime, GSSCredential.INITIATE_ONLY, cStub); } srcName = cred.getName(); + doServicePermCheck(); } } @@ -306,6 +298,9 @@ public byte[] initSecContext(InputStream is, int mechTokenLen) // getMechFromSpNegoToken() does the horrible, no good, very // bad thing its name says it does. For now we retain this bit // of evil. + // + // XXX Time to remove this workaround. It's been 20 + // years. try { actualMech = getMechFromSpNegoToken(outToken, true); } catch (GSSException e) { } @@ -326,13 +321,7 @@ public byte[] initSecContext(InputStream is, int mechTokenLen) (cStub.getContextName(pContext, true), actualMech, cStub); } - // XXX Attempt to get the actual target name from the cStub. - - if (cred == null) { - disposeCred = cred = - new GSSCredElement(srcName, lifetime, - GSSCredential.INITIATE_ONLY, cStub); - } + if (!skipServicePermCheck) doServicePermCheck(); } } return outToken; @@ -353,26 +342,15 @@ public byte[] acceptSecContext(InputStream is, int mechTokenLen) if (isEstablished && targetName == null) { targetName = new GSSNameElement (cStub.getContextName(pContext, false), actualMech, cStub); - // Replace the current default acceptor cred now that - // the context acceptor name is available - if (disposeCred != null) - disposeCred.dispose(); - disposeCred = cred = - new GSSCredElement(targetName, lifetime, - GSSCredential.ACCEPT_ONLY, cStub); } - - // Only inspect token when the permission check has not - // been performed - // - // XXX No, we should inspect the token the one time we can, just - // like with actualMech on the initiator side. - if (GSSUtil.isSpNegoMech(cStub.getMech()) && - (outToken != null) && !skipServicePermCheck) { - if (GSSUtil.isKerberosMech(getMechFromSpNegoToken - (outToken, false))) { - doServicePermCheck(); - } + if (GSSUtil.isSpNegoMech(cStub.getMech()) && outToken != null && + actualMech == null) { + try { + actualMech = getMechFromSpNegoToken(outToken, true); + } catch (GSSException e) { } + } + if (isEstablished && targetName != null && !skipServicePermCheck) { + doServicePermCheck(); } } return outToken; From 16f33b3b0310e0e932a0f71eb5ba224604656902 Mon Sep 17 00:00:00 2001 From: Nico Williams Date: Thu, 27 Sep 2018 12:43:22 -0500 Subject: [PATCH 25/25] Dispose of delegated cred handles early Native objects are memory icebergs: they are much larger than the JVM knows, so the GC might not dispose of them soon enough. --- .../sun/security/jgss/wrapper/NativeGSSContext.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java index 40cebf8e7c4..2d99c9517cf 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/NativeGSSContext.java @@ -70,6 +70,7 @@ class NativeGSSContext implements GSSContextSpi { private ChannelBinding cb; private GSSCredElement delegatedCred; + private GSSCredElement disposeDelegatedCred; private int flags; private int lifetime = GSSCredential.DEFAULT_LIFETIME; private final GSSLibStub cStub; @@ -336,6 +337,7 @@ public byte[] acceptSecContext(InputStream is, int mechTokenLen) inToken.length); long pCred = (cred == null? 0 : cred.pCred); outToken = cStub.acceptContext(pCred, cb, inToken, this); + disposeDelegatedCred = delegatedCred; SunNativeProvider.debug("acceptSecContext=> outToken len=" + (outToken == null? 0 : outToken.length)); @@ -363,7 +365,9 @@ public boolean isEstablished() { public void dispose() throws GSSException { if (disposeCred != null) disposeCred.dispose(); - disposeCred = cred = null; + if (disposeDelegatedCred != null) + disposeDelegatedCred.dispose(); + disposeDelegatedCred = disposeCred = cred = null; srcName = null; targetName = null; delegatedCred = null; @@ -629,6 +633,7 @@ public Oid getMech() throws GSSException { } } public GSSCredentialSpi getDelegCred() throws GSSException { + disposeDelegatedCred = null; return delegatedCred; } public boolean isInitiator() {