-
Notifications
You must be signed in to change notification settings - Fork 6.2k
8277489: Rewrite JAAS UnixLoginModule with FFM #28931
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| /* | ||
| * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. | ||
| * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. | ||
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| * | ||
| * This code is free software; you can redistribute it and/or modify it | ||
|
|
@@ -25,38 +25,144 @@ | |
|
|
||
| package com.sun.security.auth.module; | ||
|
|
||
| import java.lang.foreign.AddressLayout; | ||
| import java.lang.foreign.Arena; | ||
| import java.lang.foreign.FunctionDescriptor; | ||
| import java.lang.foreign.GroupLayout; | ||
| import java.lang.foreign.Linker; | ||
| import java.lang.foreign.MemoryLayout; | ||
| import java.lang.foreign.MemorySegment; | ||
| import java.lang.foreign.SymbolLookup; | ||
| import java.lang.foreign.ValueLayout; | ||
| import java.lang.invoke.MethodHandle; | ||
|
|
||
| import static java.lang.foreign.MemoryLayout.PathElement.groupElement; | ||
|
|
||
| /** | ||
| * This class implementation retrieves and makes available Unix | ||
| * UID/GID/groups information for the current user. | ||
| * | ||
| * @since 1.4 | ||
| */ | ||
| @SuppressWarnings("restricted") | ||
| public class UnixSystem { | ||
|
|
||
| private native void getUnixInfo(); | ||
|
|
||
| // Warning: the following 4 fields are used by Unix.c | ||
|
|
||
| /** The current username. */ | ||
| /** | ||
| * The current username. | ||
| */ | ||
| protected String username; | ||
|
|
||
| /** The current user ID. */ | ||
| /** | ||
| * The current user ID. | ||
| */ | ||
| protected long uid; | ||
|
|
||
| /** The current group ID. */ | ||
| /** | ||
| * The current group ID. | ||
| */ | ||
| protected long gid; | ||
|
|
||
| /** The current list of groups. */ | ||
| /** | ||
| * The current list of groups. | ||
| */ | ||
| protected long[] groups; | ||
|
|
||
| private static final Linker LINKER = Linker.nativeLinker(); | ||
| private static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup() | ||
| .or(LINKER.defaultLookup()); | ||
|
|
||
| private static final ValueLayout.OfByte C_CHAR | ||
| = (ValueLayout.OfByte) LINKER.canonicalLayouts().get("char"); | ||
| private static final ValueLayout.OfInt C_INT | ||
| = (ValueLayout.OfInt) LINKER.canonicalLayouts().get("int"); | ||
| private static final AddressLayout C_POINTER | ||
| = ((AddressLayout) LINKER.canonicalLayouts().get("void*")) | ||
| .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, C_CHAR)); | ||
| private static final ValueLayout.OfLong C_LONG | ||
| = (ValueLayout.OfLong) LINKER.canonicalLayouts().get("long"); | ||
|
|
||
| private static final MethodHandle getgroups = LINKER | ||
| .downcallHandle(SYMBOL_LOOKUP.findOrThrow("getgroups"), | ||
| FunctionDescriptor.of(C_INT, C_INT, C_POINTER)); | ||
| private static final MethodHandle getuid = LINKER | ||
| .downcallHandle(SYMBOL_LOOKUP.findOrThrow("getuid"), | ||
| FunctionDescriptor.of(C_INT)); | ||
| private static final MethodHandle getgid = LINKER | ||
| .downcallHandle(SYMBOL_LOOKUP.findOrThrow("getgid"), | ||
| FunctionDescriptor.of(C_INT)); | ||
| private static final MethodHandle getpwuid_r = LINKER | ||
| .downcallHandle(SYMBOL_LOOKUP.findOrThrow("getpwuid_r"), | ||
| FunctionDescriptor.of(C_INT, C_INT, C_POINTER, C_POINTER, | ||
| C_LONG, C_POINTER)); | ||
|
|
||
| private static final GroupLayout passwd_layout = MemoryLayout.structLayout( | ||
| C_POINTER.withName("pw_name"), | ||
| C_POINTER.withName("pw_passwd"), | ||
| C_INT.withName("pw_uid"), | ||
| C_INT.withName("pw_gid"), | ||
| // Different platforms have different fields in `struct passwd`. | ||
| // While we don't need those fields here, the struct needs to be | ||
| // big enough to avoid buffer overflow when `getpwuid_r` is called. | ||
| MemoryLayout.sequenceLayout(100, C_CHAR).withName("dummy")); | ||
|
|
||
| private static final ValueLayout.OfInt pw_uid_layout | ||
| = (ValueLayout.OfInt) passwd_layout.select(groupElement("pw_uid")); | ||
| private static final long pw_uid_offset | ||
| = passwd_layout.byteOffset(groupElement("pw_uid")); | ||
| private static final ValueLayout.OfInt pw_gid_layout | ||
| = (ValueLayout.OfInt) passwd_layout.select(groupElement("pw_gid")); | ||
| private static final long pw_gid_offset | ||
| = passwd_layout.byteOffset(groupElement("pw_gid")); | ||
| private static final AddressLayout pw_name_layout | ||
| = (AddressLayout) passwd_layout.select(groupElement("pw_name")); | ||
| private static final long pw_name_offset | ||
| = passwd_layout.byteOffset(groupElement("pw_name")); | ||
|
|
||
| // sysconf(_SC_GETPW_R_SIZE_MAX) on macOS is 4096 and 1024 on Linux | ||
| private static final long GETPW_R_SIZE_MAX = 4096L; | ||
|
|
||
| /** | ||
| * Instantiate a {@code UnixSystem} and load | ||
| * the native library to access the underlying system information. | ||
| */ | ||
| @SuppressWarnings("restricted") | ||
| public UnixSystem() { | ||
| System.loadLibrary("jaas"); | ||
| getUnixInfo(); | ||
| try (Arena scope = Arena.ofConfined()) { | ||
| int groupnum = (int) getgroups.invokeExact(0, MemorySegment.NULL); | ||
| if (groupnum == -1) { | ||
| throw new RuntimeException("getgroups returns " + groupnum); | ||
| } | ||
|
|
||
| var gs = scope.allocate(C_INT, groupnum); | ||
| groupnum = (int) getgroups.invokeExact(groupnum, gs); | ||
| if (groupnum == -1) { | ||
| throw new RuntimeException("getgroups returns " + groupnum); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a must-have, but would be nice to have a FFM for
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. |
||
| } | ||
|
|
||
| groups = new long[groupnum]; | ||
| for (int i = 0; i < groupnum; i++) { | ||
| groups[i] = gs.getAtIndex(C_INT, i); | ||
| } | ||
|
|
||
| var resbuf = scope.allocate(passwd_layout); | ||
| var pwd = scope.allocate(C_POINTER); | ||
| var pwd_buf = scope.allocate(GETPW_R_SIZE_MAX); | ||
| var tmpUid = (int)getuid.invokeExact(); | ||
| int out = (int) getpwuid_r.invokeExact( | ||
| tmpUid, resbuf, pwd_buf, GETPW_R_SIZE_MAX, pwd); | ||
| if (out != 0 || pwd.get(ValueLayout.ADDRESS, 0).equals(MemorySegment.NULL)) { | ||
| uid = tmpUid; | ||
| gid = (int)getgid.invokeExact(); | ||
| username = null; | ||
| } else { | ||
| uid = resbuf.get(pw_uid_layout, pw_uid_offset); | ||
| gid = resbuf.get(pw_gid_layout, pw_gid_offset); | ||
| username = resbuf.get(pw_name_layout, pw_name_offset).getString(0); | ||
| } | ||
| } catch (Throwable t) { | ||
| var error = new UnsatisfiedLinkError("FFM calls failed"); | ||
| error.initCause(t); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you want to check with getCause() here as well?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| throw error; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
|
|
||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a must-have, but would be nice to have a FFM for
errnoto provide better diagnostics.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. There is a
Linker.Option.captureCallState("errno")feature and I'll see how to use it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like there has been prior work with this, at least with macos, e.g.: src/java.base/macosx/classes/jdk/internal/ffi/generated/errno/errno_h.java