X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.enterprise%2Fsrc%2Forg%2Fargeo%2Fosgi%2Fuseradmin%2FLdifUser.java;h=392b17428b00b748be25de7f2c8b04000438c120;hb=088c1b517a543e935d8ab65c3b2fd2d0269b551d;hp=d26ed148f0f861aa22ed0661f56339c4d63da381;hpb=0243aa5633af84d8608ba912483dbaaaefac42f1;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUser.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUser.java index d26ed148f..392b17428 100644 --- a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUser.java +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdifUser.java @@ -1,9 +1,6 @@ package org.argeo.osgi.useradmin; -import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; @@ -22,7 +19,9 @@ import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttribute; import javax.naming.ldap.LdapName; +import org.argeo.naming.AuthPassword; import org.argeo.naming.LdapAttrs; +import org.argeo.naming.SharedSecret; /** Directory user implementation */ class LdifUser implements DirectoryUser { @@ -73,11 +72,72 @@ class LdifUser implements DirectoryUser { public boolean hasCredential(String key, Object value) { if (key == null) { // TODO check other sources (like PKCS12) - char[] password = toChars(value); + // String pwd = new String((char[]) value); + // authPassword (RFC 312 https://tools.ietf.org/html/rfc3112) + char[] password = DigestUtils.bytesToChars(value); + AuthPassword authPassword = AuthPassword.matchAuthValue(getAttributes(), password); + if (authPassword != null) { + if (authPassword.getAuthScheme().equals(SharedSecret.X_SHARED_SECRET)) { + SharedSecret onceToken = new SharedSecret(authPassword); + if (onceToken.isExpired()) { + // AuthPassword.remove(getAttributes(), onceToken); + return false; + } else { + // boolean wasRemoved = AuthPassword.remove(getAttributes(), onceToken); + return true; + } + // TODO delete expired tokens? + } else { + // TODO implement SHA + throw new UnsupportedOperationException( + "Unsupported authPassword scheme " + authPassword.getAuthScheme()); + } + } + + // Regular password byte[] hashedPassword = hash(password); - return hasCredential(LdapAttrs.userPassword.name(), hashedPassword); + if (hasCredential(LdapAttrs.userPassword.name(), hashedPassword)) + return true; + // if (hasCredential(LdapAttrs.authPassword.name(), pwd)) + // return true; + return false; } + // authPassword (RFC 3112 https://tools.ietf.org/html/rfc3112) + // if (key.startsWith(ClientToken.X_CLIENT_TOKEN)) { + // return ClientToken.checkAttribute(getAttributes(), key, value); + // } else if (key.startsWith(OnceToken.X_ONCE_TOKEN)) { + // return OnceToken.checkAttribute(getAttributes(), key, value); + // } + // StringTokenizer st = new StringTokenizer((String) storedValue, "$ "); + // // TODO make it more robust, deal with bad formatting + // String authScheme = st.nextToken(); + // String authInfo = st.nextToken(); + // String authValue = st.nextToken(); + // if (authScheme.equals(UriToken.X_URI_TOKEN)) { + // UriToken token = new UriToken((String)storedValue); + // try { + // URI uri = new URI(authInfo); + // Map> query = NamingUtils.queryToMap(uri); + // String expiryTimestamp = NamingUtils.getQueryValue(query, + // LdapAttrs.modifyTimestamp.name()); + // if (expiryTimestamp != null) { + // Instant expiryOdt = NamingUtils.ldapDateToInstant(expiryTimestamp); + // if (expiryOdt.isBefore(Instant.now())) + // return false; + // } else { + // throw new UnsupportedOperationException("An expiry timestamp " + // + LdapAttrs.modifyTimestamp.name() + " must be set in the URI query"); + // } + // byte[] hash = Base64.getDecoder().decode(authValue); + // byte[] hashedInput = DigestUtils.sha1((authInfo + + // value).getBytes(StandardCharsets.US_ASCII)); + // return Arrays.equals(hash, hashedInput); + // } catch (URISyntaxException e) { + // throw new UserDirectoryException("Badly formatted " + authInfo, e); + // } + // } + Object storedValue = getCredentials().get(key); if (storedValue == null || value == null) return false; @@ -92,35 +152,38 @@ class LdifUser implements DirectoryUser { /** Hash and clear the password */ private byte[] hash(char[] password) { - byte[] hashedPassword = ("{SHA}" + Base64.getEncoder().encodeToString(DigestUtils.sha1(toBytes(password)))) - .getBytes(); - Arrays.fill(password, '\u0000'); + byte[] hashedPassword = ("{SHA}" + + Base64.getEncoder().encodeToString(DigestUtils.sha1(DigestUtils.charsToBytes(password)))) + .getBytes(StandardCharsets.UTF_8); + // Arrays.fill(password, '\u0000'); return hashedPassword; } - private byte[] toBytes(char[] chars) { - CharBuffer charBuffer = CharBuffer.wrap(chars); - ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer); - byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit()); - Arrays.fill(charBuffer.array(), '\u0000'); // clear sensitive data - Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data - return bytes; - } - - private char[] toChars(Object obj) { - if (obj instanceof char[]) - return (char[]) obj; - if (!(obj instanceof byte[])) - throw new IllegalArgumentException(obj.getClass() + " is not a byte array"); - ByteBuffer fromBuffer = ByteBuffer.wrap((byte[]) obj); - CharBuffer toBuffer = Charset.forName("UTF-8").decode(fromBuffer); - char[] res = Arrays.copyOfRange(toBuffer.array(), toBuffer.position(), toBuffer.limit()); - Arrays.fill(fromBuffer.array(), (byte) 0); // clear sensitive data - Arrays.fill((byte[]) obj, (byte) 0); // clear sensitive data - Arrays.fill(toBuffer.array(), '\u0000'); // clear sensitive data - return res; - } - + // private byte[] toBytes(char[] chars) { + // CharBuffer charBuffer = CharBuffer.wrap(chars); + // ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(charBuffer); + // byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), + // byteBuffer.limit()); + // // Arrays.fill(charBuffer.array(), '\u0000'); // clear sensitive data + // Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data + // return bytes; + // } + // + // private char[] toChars(Object obj) { + // if (obj instanceof char[]) + // return (char[]) obj; + // if (!(obj instanceof byte[])) + // throw new IllegalArgumentException(obj.getClass() + " is not a byte array"); + // ByteBuffer fromBuffer = ByteBuffer.wrap((byte[]) obj); + // CharBuffer toBuffer = StandardCharsets.UTF_8.decode(fromBuffer); + // char[] res = Arrays.copyOfRange(toBuffer.array(), toBuffer.position(), + // toBuffer.limit()); + // Arrays.fill(fromBuffer.array(), (byte) 0); // clear sensitive data + // Arrays.fill((byte[]) obj, (byte) 0); // clear sensitive data + // Arrays.fill(toBuffer.array(), '\u0000'); // clear sensitive data + // return res; + // } + // @Override public LdapName getDn() { return dn; @@ -255,7 +318,7 @@ class LdifUser implements DirectoryUser { if (key.equals(LdapAttrs.userPassword.name())) // TODO other cases (certificates, images) return value; - value = new String((byte[]) value, Charset.forName("UTF-8")); + value = new String((byte[]) value, StandardCharsets.UTF_8); } if (attr.size() == 1) return value; @@ -284,10 +347,13 @@ class LdifUser implements DirectoryUser { public Object put(String key, Object value) { if (key == null) { // TODO persist to other sources (like PKCS12) - char[] password = toChars(value); + char[] password = DigestUtils.bytesToChars(value); byte[] hashedPassword = hash(password); return put(LdapAttrs.userPassword.name(), hashedPassword); } + if (key.startsWith("X-")) { + return put(LdapAttrs.authPassword.name(), value); + } userAdmin.checkEdit(); if (!isEditing()) @@ -303,13 +369,10 @@ class LdifUser implements DirectoryUser { try { Attribute attribute = getModifiedAttributes().get(key.toString()); + // if (attribute == null) // block unit tests attribute = new BasicAttribute(key.toString()); if (value instanceof String && !isAsciiPrintable(((String) value))) - try { - attribute.add(((String) value).getBytes("UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new UserDirectoryException("Cannot encode " + value, e); - } + attribute.add(((String) value).getBytes(StandardCharsets.UTF_8)); else attribute.add(value); Attribute previousAttribute = getModifiedAttributes().put(attribute);