Bug 228503 Security should hook into the OS keychain on OS X
diff --git a/bundles/org.eclipse.equinox.security.macosx/keystoreNative/src/keystoreNativejnilib.c b/bundles/org.eclipse.equinox.security.macosx/keystoreNative/src/keystoreNativejnilib.c
new file mode 100644
index 0000000..8c7f3cf
--- /dev/null
+++ b/bundles/org.eclipse.equinox.security.macosx/keystoreNative/src/keystoreNativejnilib.c
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+#include <jni.h>
+#include "keystoreNative.h"
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+#include <CoreServices/CoreServices.h>
+
+/**
+ * Implements the get password functionality.
+ */
+jstring getPassword(JNIEnv *env, jobject this, jstring serviceName, jstring accountName) {
+ OSStatus status;
+ UInt32 passwordLength = (UInt32) nil;
+ void *passwordData = nil;
+ const char *serviceNameUTF = (*env)->GetStringUTFChars(env, serviceName, NULL);
+ const char *accountNameUTF = (*env)->GetStringUTFChars(env, accountName, NULL);
+
+ status = SecKeychainFindGenericPassword (NULL,
+ (*env)->GetStringUTFLength(env, serviceName), serviceNameUTF,
+ (*env)->GetStringUTFLength(env, accountName), accountNameUTF,
+ &passwordLength, &passwordData,
+ NULL
+ );
+
+ // free the UTF strings
+ (*env)->ReleaseStringUTFChars( env, serviceName, serviceNameUTF );
+ (*env)->ReleaseStringUTFChars( env, accountName, accountNameUTF );
+
+ // throw an exception if we have an error
+ if (status != noErr) {
+ (*env)->ExceptionClear(env);
+ char buffer [60];
+ sprintf(buffer, "Could not obtain password. Result: %d", (int) status);
+ (*env)->ThrowNew(env, (* env)->FindClass(env, "java/lang/SecurityException"), buffer);
+ }
+
+ // massage the string into a Java-friendly UTF-8 string
+ char *truncatedPassword = (char *) malloc(passwordLength * sizeof(char) + 1);
+ strncpy(truncatedPassword, passwordData, passwordLength * sizeof(char));
+ truncatedPassword[passwordLength * sizeof(char)] = '\0';
+ jstring result = (*env)->NewStringUTF(env, truncatedPassword);
+ free(truncatedPassword);
+ // free the returned password data
+ SecKeychainItemFreeContent (NULL, passwordData);
+ return result;
+}
+
+JNIEXPORT jstring JNICALL Java_org_eclipse_equinox_internal_security_osx_OSXProvider_getPassword(JNIEnv *env, jobject this, jstring serviceName, jstring accountName) {
+ return getPassword(env, this, serviceName, accountName);
+}
+
+/**
+ * Implements the set password functionality.
+ */
+void setPassword(JNIEnv *env, jobject this, jstring serviceName, jstring accountName, jstring password) {
+ OSStatus status;
+ const char *serviceNameUTF = (*env)->GetStringUTFChars(env, serviceName, NULL);
+ const char *accountNameUTF = (*env)->GetStringUTFChars(env, accountName, NULL);
+ const char *passwordUTF = (*env)->GetStringUTFChars(env, password, NULL);
+
+ // attempt to add the password
+ status = SecKeychainAddGenericPassword (NULL,
+ (*env)->GetStringUTFLength(env, serviceName), serviceNameUTF,
+ (*env)->GetStringUTFLength(env, accountName), accountNameUTF,
+ (*env)->GetStringUTFLength(env, password), passwordUTF,
+ NULL);
+
+ // it already exists, try to change it
+ if (status == errSecDuplicateItem) {
+ SecKeychainItemRef itemRef = (SecKeychainItemRef) nil;
+
+ // find the ItemRef corresponding to the item
+ status = SecKeychainFindGenericPassword (NULL,
+ (*env)->GetStringUTFLength(env, serviceName), serviceNameUTF,
+ (*env)->GetStringUTFLength(env, accountName), accountNameUTF,
+ NULL, NULL,
+ &itemRef
+ );
+
+ // this should rarely happen - we're in this state because it exists.
+ if (status != noErr) {
+ // free the UTF strings
+ (*env)->ReleaseStringUTFChars( env, serviceName, serviceNameUTF );
+ (*env)->ReleaseStringUTFChars( env, accountName, accountNameUTF );
+ (*env)->ReleaseStringUTFChars( env, password, passwordUTF );
+ // release the pointer to the item
+ // the following code craps out for some unknown reason when called from within Eclipse. It works fine in a standalone app, however.
+ //if (itemRef) CFRelease(itemRef);
+
+ (*env)->ExceptionClear(env);
+ char buffer [60];
+ sprintf(buffer, "Could not obtain password. Result: %d", (int) status);
+ (*env)->ThrowNew(env, (* env)->FindClass(env, "java/lang/SecurityException"), buffer);
+ }
+
+ // modify the data based on the item we've gotten above
+ status = SecKeychainItemModifyAttributesAndData (itemRef, NULL, (*env)->GetStringUTFLength(env, password), passwordUTF);
+
+ // free the UTF strings
+ (*env)->ReleaseStringUTFChars( env, serviceName, serviceNameUTF );
+ (*env)->ReleaseStringUTFChars( env, accountName, accountNameUTF );
+ (*env)->ReleaseStringUTFChars( env, password, passwordUTF );
+
+ // release the pointer to the item
+ // the following code craps out for some unknown reason when called from within Eclipse. It works fine in a standalone app, however.
+ //if (itemRef) CFRelease(itemRef);
+
+ // throw an exception if it didnt work
+ if (status != noErr) {
+ (*env)->ExceptionClear(env);
+ char buffer [60];
+ sprintf(buffer, "Could change password. Result: %d", (int) status);
+ (*env)->ThrowNew(env, (* env)->FindClass(env, "java/lang/SecurityException"), buffer);
+ }
+
+ }
+}
+
+JNIEXPORT void JNICALL Java_org_eclipse_equinox_internal_security_osx_OSXProvider_setPassword(JNIEnv *env, jobject this, jstring serviceName, jstring accountName, jstring password) {
+ setPassword(env, this, serviceName, accountName, password);
+}
+