Reputation: 8584
void**
pointer (that is really a char**
), and how do I access the result from it?I'm trying to get a password from macOS Keychain in Java using this C function:
OSStatus SecKeychainFindGenericPassword(CFTypeRef keychainOrArray,
UInt32 serviceNameLength,
const char *serviceName,
UInt32 accountNameLength,
const char *accountName,
UInt32 *passwordLength,
void * _Nullable *passwordData,
SecKeychainItemRef _Nullable *itemRef);
The problem is, I'm not sure how to handle the passwordData
parameter. I managed to call it from C (see bottom of post). But, in Java, I'm getting nondeterministic and incorrect results for the passwordData
parameter. Here's my attempt to call it from Java:
import com.sun.jna.*;
import com.sun.jna.ptr.*;
public class JnaTest {
public interface Security extends Library {
int SecKeychainFindGenericPassword(
Object keychainOrArray,
int serviceNameLength,
String serviceName,
int accountNameLength,
String accountName,
IntByReference passwordLength,
// I've also tried char[][] and Pointer, which both give errors
PointerByReference passwordData,
Object itemRef);
}
public static void main(String[] args) {
Security sec = Native.loadLibrary("Security", Security.class);
PointerByReference pass = new PointerByReference();
IntByReference len = new IntByReference();
int rc = sec.SecKeychainFindGenericPassword(
null,
10, "SurfWriter",
10, "MyUserAcct",
len, pass,
null);
System.out.println(rc); // 0, good
System.out.println(len.getValue()); // 11, same as in C
// This prints Unicode characters nondeterministically; buffer overrun?
System.out.println(new String(pass.getValue().getCharArray(0, len.getValue())));
}
}
I was able to write this C program to do the same thing, and it works fine, so the problem is almost definitely with reading passwordData
in JNA:
#include <Security/Security.h>
int main() {
unsigned int len;
void* pass;
int rc = SecKeychainFindGenericPassword(
NULL,
10, "SurfWriter",
10, "MyUserAcct",
&len, &pass,
NULL);
printf("%d %s\n", len, pass);
}
Upvotes: 2
Views: 377
Reputation: 8584
It turns out that it works if I do this:
pass.getValue().getString(0);
It works. Perfectly. Unfortunately, this is not a complete solution, as I would like to avoid storing passwords in String
s for security reasons. Playing around some more, I found that this works:
new String(pass.getValue().getByteArray(0, len.getValue()-4));
(The -4
is because len
is too large by 4 for some reason; I can't find anything on this in the docs for Security.h
.) I realized that the "random" passwords it was printing before were only random in the second half, which leads me to a conclusion: Java uses 16-bit char
s internally, so each of those characters I was getting was really two characters, and I was reading twice as far as necessary. Java was treating 2 ASCII characters as one UTF-16 character. (Shouldn't JNA handle this?)
Upvotes: 1
Reputation: 124
I don't have possibility to test it (as I am currently on Windows), but what if you try retrieve password like:
String password = new String(pass.getValue().getPointer(0).getCharArray(0, len.getValue()));
It is only an idea, for more useful answer I would need to debug the code.
Still, I am puzzled, why it returns 11 for length.
Upvotes: 0