Brian McCutchon
Brian McCutchon

Reputation: 8584

Get result of function by void** parameter as char[] with JNA

TL;DR: What JNA type do I use for a 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);

(Usage example.)

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

Answers (2)

Brian McCutchon
Brian McCutchon

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 Strings 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 chars 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

Martin Polak
Martin Polak

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

Related Questions