java - How to map Windows API CredWrite/CredRead in JNA? -
i'm trying map credwrite/credread in jna in order store thrid party credential used in java application in windows credential manager (os windows 10).
here're original signatures in c:
// https://msdn.microsoft.com/en-us/library/aa375187(v=vs.85).aspx bool credwrite( _in_ pcredential credential, _in_ dword flags ); // https://msdn.microsoft.com/en-us/library/aa374804(v=vs.85).aspx bool credread( _in_ lpctstr targetname, _in_ dword type, _in_ dword flags, _out_ pcredential *credential ); typedef struct _credential { dword flags; dword type; lptstr targetname; lptstr comment; filetime lastwritten; dword credentialblobsize; lpbyte credentialblob; dword persist; dword attributecount; pcredential_attribute attributes; lptstr targetalias; lptstr username; } credential, *pcredential; typedef struct _credential_attribute { lptstr keyword; dword flags; dword valuesize; lpbyte value; } credential_attribute, *pcredential_attribute;
here're maps in java:
wincrypt instance = (wincrypt) native.loadlibrary("advapi32", wincrypt.class, w32apioptions.default_options); public boolean credwrite( credential.byreference credential, int flags ); public boolean credread( string targetname, int type, int flags, pointerbyreference credential ); public static class credential extends structure { public int flags; public int type; public string targetname; public string comment; public filetime lastwritten; public int credentialblobsize; public byte[] credentialblob = new byte[128]; public int persist; public int attributecount; public credential_attribute.byreference attributes; public string targetalias; public string username; public static class byreference extends credential implements structure.byreference { public byreference() { } public byreference(pointer memory) { super(memory); // line 55 } } public credential() { super(); } public credential(pointer memory) { super(memory); read(); // line 65 } @override protected list<string> getfieldorder() { return arrays.aslist(new string[] { "flags", "type", "targetname", "comment", "lastwritten", "credentialblobsize", "credentialblob", "persist", "attributecount", "attributes", "targetalias", "username" }); } } public static class credential_attribute extends structure { public string keyword; public int flags; public int valuesize; public byte[] value = new byte[128]; public static class byreference extends credential_attribute implements structure.byreference { } @override protected list<string> getfieldorder() { return arrays.aslist(new string[] { "keyword", "flags", "valuesize", "value" }); } }
first tried write credential windows credential manager:
string password = "passwordtest"; int cbcreds = 1 + password.length(); credential.byreference credref = new credential.byreference(); credref.type = wincrypt.cred_type_generic; credref.targetname = "test/account"; credref.credentialblobsize = cbcreds; credref.credentialblob = password.getbytes(); credref.persist = wincrypt.cred_persist_local_machine; credref.username = "administrator"; boolean ok = wincrypt.instance.credwrite(credref, 0); int rc = kernel32.instance.getlasterror(); string errmsg = kernel32util.formatmessage(rc); system.out.println("credwrite() - ok: " + ok + ", errno: " + rc + ", errmsg: " + errmsg);
output of try write:
credwrite() - ok: false, errno: 87, errmsg: parameter incorrect.
then tried read existing credential windows credential manager:
pointerbyreference pref = new pointerbyreference(); boolean ok = wincrypt.instance.credread("build-apps", wincrypt.cred_type_domain_password, 0, pref); int rc = kernel32.instance.getlasterror(); string errmsg = kernel32util.formatmessage(rc); system.out.println("credread() - ok: " + ok + ", errno: " + rc + ", errmsg: " + errmsg); credential cred = new credential.byreference(pref.getpointer()); // line 44
output of try read:
credread() - ok: true, errno: 0, errmsg: operation completed successfully. exception in thread "main" java.lang.illegalargumentexception: structure exceeds provided memory bounds @ com.sun.jna.structure.ensureallocated(structure.java:366) @ com.sun.jna.structure.ensureallocated(structure.java:346) @ com.sun.jna.structure.read(structure.java:552) @ com.abc.crypt.wincrypt$credential.<init>(wincrypt.java:65) @ com.abc.crypt.wincrypt$credential$byreference.<init>(wincrypt.java:55) @ com.abc.crypt.crypttest.main(crypttest.java:44) caused by: java.lang.indexoutofboundsexception: bounds exceeds available space : size=8, offset=200 @ com.sun.jna.memory.boundscheck(memory.java:203) @ com.sun.jna.memory$sharedmemory.boundscheck(memory.java:87) @ com.sun.jna.memory.share(memory.java:131) @ com.sun.jna.structure.ensureallocated(structure.java:363) ... 5 more
so try write failed, try read succeeded failed create credential object based on output.
according webpage of credwrite api, errno 87 got in write test following error:
error_invalid_parameter
certain fields cannot changed in existing credential. error returned if field not match value in protected field of existing credential.
however value put in credential instance new credential rather existing 1 in windows credential manager.
any suggestion or idea on how fix/improve appreciated.
===================================
update after applying fix:
new credread:
public boolean credread( string targetname, int type, int flags, credential.byreference credential );
test credread:
credential.byreference pref = new credential.byreference(); boolean ok = wincrypt.instance.credread("test/account", wincrypt.cred_type_generic, 0, pref); int rc = kernel32.instance.getlasterror(); string errmsg = kernel32util.formatmessage(rc); system.out.println("credread() - ok: " + ok + ", errno: " + rc + ", errmsg: " + errmsg); system.out.println(string.format("read username = '%s', password='%s' (%d bytes)\n", pref.username, pref.credentialblob, pref.credentialblobsize));
result:
credread() - ok: true, errno: 0, errmsg: operation completed successfully. read username = 'null', password='null' (0 bytes)
i checked how jna samples in contrib use byreference on out arg , doing in same way newing byreference , pass function.
credread.pcredential
should credential.byreference
. using pointerbyreference
ends passing in pointer null value instead of expected pointer credential
struct.
credental.credentialblob
needs pointer
or pointertype
(probably memory
if you're initializing block yourself). using inline byte array shifts entire structure array size, callee expecting pointer block of memory.
update
i think misread declaration of credread()
.
credread
should continue use pointerbyreference
. use pointerbyreference.getvalue()
extract "returned" pointer value credread()
in order create new credentials
instance based on pointer. pointerbyreference.getpointer()
gives address of memory allocated hold pointer value.
public boolean credwrite( credential credential, int flags ); public boolean credread( string targetname, int type, int flags, pointerbyreference pref ); pointerbyreference pref = new pointerbyreference() credread(name, type, flags, pref); creds = new credentials(pref.getvalue())
Comments
Post a Comment