package org.dcache.gplazma.plugins; import com.google.common.collect.Sets; import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.ptr.IntByReference; import java.security.Principal; import java.util.List; import java.util.Properties; import java.util.Set; import org.dcache.auth.GidPrincipal; import org.dcache.auth.GroupNamePrincipal; import org.dcache.auth.UidPrincipal; import org.dcache.auth.UserNamePrincipal; import org.dcache.auth.attributes.HomeDirectory; import org.dcache.auth.attributes.RootDirectory; import org.dcache.gplazma.AuthenticationException; import org.dcache.gplazma.NoSuchPrincipalException; import static com.google.common.collect.Iterables.filter; import static java.util.Arrays.asList; /** * {@code GPlazmaMappingPlugin} and {@code GPlazmaIdentityPlugin} implementation for * Unix based systems. The actual mapping happens according to systems {@literal /etc/nsswith.conf} * configuration. The following mapping takes place: * <pre> * login name: {@link UserNamePrincipal} * uid : {@link UidPrincipal} * gid : {@link GidPrincipal}, primary * other gids: {@link GidPrincipal} * </pre> */ public class Nsswitch implements GPlazmaMappingPlugin, GPlazmaIdentityPlugin, GPlazmaSessionPlugin { /** * handle to libc. */ private final LibC _libc; /** * Create an instance of {@link Nsswitch} gplazma plugin. * * @param properties * @throws UnsatisfiedLinkError if unable to load system libraries. */ public Nsswitch(Properties properties) throws UnsatisfiedLinkError { _libc = (LibC) Native.loadLibrary("c", LibC.class); } Nsswitch(LibC libc) { _libc = libc; } private __password findPasswordRecord(Set<Principal> principals) { for (UserNamePrincipal principal: filter(principals, UserNamePrincipal.class)) { __password p = _libc.getpwnam(principal.getName()); if (p != null) { return p; } } return null; } @Override public void map(Set<Principal> principals) throws AuthenticationException { __password p = findPasswordRecord(principals); if (p != null) { principals.add(new UidPrincipal(p.uid)); principals.add(new GidPrincipal(p.gid, true)); int[] gids = groupsOf(p); for (int id : gids) { principals.add(new GidPrincipal(id, false)); } } else { throw new AuthenticationException("no mapping"); } } /** * Maps {@link UserNamePrincipal} to corresponding {@link UidPrincipal} and * {@link GroupNamePrincipal} to corresponding {@link GidPrincipal}. * @param principal to map * @return mapped principal. * @throws NoSuchPrincipalException if user or group name does can't be mapped. */ @Override public Principal map(Principal principal) throws NoSuchPrincipalException { if (principal instanceof UserNamePrincipal) { __password p = _libc.getpwnam(principal.getName()); if (p != null) { return new UidPrincipal(p.uid); } } else if (principal instanceof GroupNamePrincipal) { __group g = _libc.getgrnam(principal.getName()); if (g != null) { return new GidPrincipal(g.gid, false); } } throw new NoSuchPrincipalException(principal); } /** * Maps {@link UidPrincipal} to corresponding {@link UserNamePrincipal} and * {@link GidPrincipal} to corresponding {@link GroupNamePrincipal}. * @param principal to map * @return mapped principal * @throws NoSuchPrincipalException if uid or gid can't be mapped. */ @Override public Set<Principal> reverseMap(Principal principal) throws NoSuchPrincipalException { if (principal instanceof UidPrincipal) { __password p = _libc.getpwuid((int) ((UidPrincipal) principal).getUid()); if (p != null) { return Sets.newHashSet((Principal)new UserNamePrincipal(p.name)); } } else if (principal instanceof GidPrincipal) { __group g = _libc.getgrgid((int) ((GidPrincipal) principal).getGid()); if (g != null) { return Sets.newHashSet((Principal)new GroupNamePrincipal(g.name)); } } throw new NoSuchPrincipalException(principal); } @Override public void session(Set<Principal> authorizedPrincipals, Set<Object> attrib) throws AuthenticationException { attrib.add(new HomeDirectory("/")); attrib.add(new RootDirectory("/")); } private int[] groupsOf(__password pwrecord) { boolean done = false; int[] groups = new int[0]; while (!done) { IntByReference ngroups = new IntByReference(); ngroups.setValue(groups.length); if (_libc.getgrouplist(pwrecord.name, pwrecord.gid, groups, ngroups) < 0) { groups = new int[ngroups.getValue()]; continue; } done = true; } return groups; } /* * struct passwd equivalent as defined in <pwd.h> */ public static class __password extends Structure { public String name; public String passwd; public int uid; public int gid; public String gecos; public String dir; public String shell; @Override protected List<String> getFieldOrder() { return asList("name", "passwd", "uid", "gid", "gecos", "dir", "shell"); } } /* * struct group equivalent as defined in <pwd.h> */ public static class __group extends Structure { public String name; public String passwd; public int gid; public Pointer mem; @Override protected List<String> getFieldOrder() { return asList("name", "passwd", "gid", "mem"); } } /* * hook required functions from libc */ public interface LibC extends Library { __password getpwnam(String name); __password getpwuid(int id); __group getgrnam(String name); __group getgrgid(int id); int getgrouplist(String user, int gid, int[] groups, IntByReference ngroups); } }