1 /*
   2  * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #import <Cocoa/Cocoa.h>
  27 #import <SystemConfiguration/SystemConfiguration.h>
  28 #import "jni_util.h"
  29 
  30 #define KERBEROS_DEFAULT_REALMS @"Kerberos-Default-Realms"
  31 #define KERBEROS_DEFAULT_REALM_MAPPINGS @"Kerberos-Domain-Realm-Mappings"
  32 #define KERBEROS_REALM_INFO @"Kerberos:%@"
  33 
  34 JavaVM *localVM;
  35 
  36 void _SCDynamicStoreCallBack(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info) {
  37     NSArray *keys = (NSArray *)changedKeys;
  38     if ([keys count] == 0) return;
  39     if (![keys containsObject:KERBEROS_DEFAULT_REALMS] && ![keys containsObject:KERBEROS_DEFAULT_REALM_MAPPINGS]) return;
  40 
  41     JNIEnv *env;
  42     bool createdFromAttach = FALSE;
  43     jint status = (*localVM)->GetEnv(localVM, (void**)&env, JNI_VERSION_1_2);
  44     if (status == JNI_EDETACHED) {
  45         status = (*localVM)->AttachCurrentThreadAsDaemon(localVM, (void**)&env, NULL);
  46         createdFromAttach = TRUE;
  47     }
  48     if (status == 0) {
  49         jclass jc_Config = (*env)->FindClass(env, "sun/security/krb5/Config");
  50         CHECK_NULL(jc_Config);
  51         jmethodID jm_Config_refresh = (*env)->GetStaticMethodID(env, jc_Config, "refresh", "()V");
  52         CHECK_NULL(jm_Config_refresh);
  53         (*env)->CallStaticVoidMethod(env, jc_Config, jm_Config_refresh);
  54         if ((*env)->ExceptionOccurred(env) != NULL) {
  55             (*env)->ExceptionClear(env);
  56         }
  57         if (createdFromAttach) {
  58             (*localVM)->DetachCurrentThread(localVM);
  59         }
  60     }
  61 }
  62 
  63 /*
  64  * Class:     sun_security_krb5_SCDynamicStoreConfig
  65  * Method:    installNotificationCallback
  66  */
  67 JNIEXPORT void JNICALL Java_sun_security_krb5_SCDynamicStoreConfig_installNotificationCallback(JNIEnv *env, jclass klass) {
  68     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; \
  69     @try {
  70         (*env)->GetJavaVM(env, &localVM);
  71         SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("java"), _SCDynamicStoreCallBack, NULL);
  72         if (store == NULL) {
  73             return;
  74         }
  75 
  76         NSArray *keys = [NSArray arrayWithObjects:KERBEROS_DEFAULT_REALMS, KERBEROS_DEFAULT_REALM_MAPPINGS, nil];
  77         SCDynamicStoreSetNotificationKeys(store, (CFArrayRef) keys, NULL);
  78 
  79         CFRunLoopSourceRef rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
  80         if (rls != NULL) {
  81             CFRunLoopAddSource(CFRunLoopGetMain(), rls, kCFRunLoopDefaultMode);
  82             CFRelease(rls);
  83         }
  84 
  85         CFRelease(store);
  86     } @catch (NSException *e) {
  87         NSLog(@"%@", [e callStackSymbols]);
  88     } @finally {
  89         [pool drain];
  90     }
  91 }
  92 
  93 #define ADD(list, str) { \
  94     jobject localeObj = (*env)->NewStringUTF(env, [str UTF8String]); \
  95     (*env)->CallBooleanMethod(env, list, jm_listAdd, localeObj); \
  96     (*env)->DeleteLocalRef(env, localeObj); \
  97 }
  98 
  99 #define ADDNULL(list) (*env)->CallBooleanMethod(env, list, jm_listAdd, NULL)
 100 
 101 /*
 102  * Class:     sun_security_krb5_SCDynamicStoreConfig
 103  * Method:    getKerberosConfig
 104  * Signature: ()Ljava/util/List;
 105  */
 106 JNIEXPORT jobject JNICALL Java_sun_security_krb5_SCDynamicStoreConfig_getKerberosConfig(JNIEnv *env, jclass klass) {
 107 
 108     jobject newList = 0;
 109 
 110     SCDynamicStoreRef store = NULL;
 111     CFTypeRef realms = NULL;
 112     CFTypeRef realmMappings = NULL;
 113     CFTypeRef realmInfo = NULL;
 114 
 115     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; \
 116     @try {
 117         SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("java-kerberos"), NULL, NULL);
 118         if (store == NULL) {
 119             return NULL;
 120         }
 121 
 122         CFTypeRef realms = SCDynamicStoreCopyValue(store, (CFStringRef) KERBEROS_DEFAULT_REALMS);
 123         if (realms == NULL || CFGetTypeID(realms) != CFArrayGetTypeID()) {
 124             return NULL;
 125         }
 126 
 127         // This methods returns a ArrayList<String>:
 128         // (realm kdc* null) null (mapping-domain mapping-realm)*
 129         jclass jc_arrayListClass = (*env)->FindClass(env, "java/util/ArrayList");
 130         CHECK_NULL_RETURN(jc_arrayListClass, NULL);
 131         jmethodID jm_arrayListCons = (*env)->GetMethodID(env, jc_arrayListClass, "<init>", "()V");
 132         CHECK_NULL_RETURN(jm_arrayListCons, NULL);
 133         jmethodID jm_listAdd = (*env)->GetMethodID(env, jc_arrayListClass, "add", "(Ljava/lang/Object;)Z");
 134         CHECK_NULL_RETURN(jm_listAdd, NULL);
 135         newList = (*env)->NewObject(env, jc_arrayListClass, jm_arrayListCons);
 136         CHECK_NULL_RETURN(newList, NULL);
 137 
 138         for (NSString *realm in (NSArray*)realms) {
 139             if (realmInfo) CFRelease(realmInfo); // for the previous realm
 140             realmInfo = SCDynamicStoreCopyValue(store, (CFStringRef) [NSString stringWithFormat:KERBEROS_REALM_INFO, realm]);
 141             if (realmInfo == NULL || CFGetTypeID(realmInfo) != CFDictionaryGetTypeID()) {
 142                 continue;
 143             }
 144 
 145             ADD(newList, realm);
 146             NSDictionary* ri = (NSDictionary*)realmInfo;
 147             for (NSDictionary* k in (NSArray*)ri[@"kdc"]) {
 148                 ADD(newList, k[@"host"]);
 149             }
 150             ADDNULL(newList);
 151         }
 152         ADDNULL(newList);
 153 
 154         CFTypeRef realmMappings = SCDynamicStoreCopyValue(store, (CFStringRef) KERBEROS_DEFAULT_REALM_MAPPINGS);
 155         if (realmMappings != NULL && CFGetTypeID(realmMappings) == CFArrayGetTypeID()) {
 156             for (NSDictionary* d in (NSArray *)realmMappings) {
 157                 for (NSString* s in d) {
 158                     ADD(newList, s);
 159                     ADD(newList, d[s]);
 160                 }
 161             }
 162         }
 163     } @catch (NSException *e) {
 164         NSLog(@"%@", [e callStackSymbols]);
 165     } @finally {
 166         [pool drain];
 167         if (realmInfo) CFRelease(realmInfo);
 168         if (realmMappings) CFRelease(realmMappings);
 169         if (realms) CFRelease(realms);
 170         if (store) CFRelease(store);
 171     }
 172     return newList;
 173 }