diff options
Diffstat (limited to 'libjava/classpath/gnu/javax/crypto/sasl/plain')
5 files changed, 779 insertions, 0 deletions
diff --git a/libjava/classpath/gnu/javax/crypto/sasl/plain/PasswordFile.java b/libjava/classpath/gnu/javax/crypto/sasl/plain/PasswordFile.java new file mode 100644 index 000000000..51542d2b2 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/plain/PasswordFile.java @@ -0,0 +1,245 @@ +/* PasswordFile.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.plain; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.security.action.GetPropertyAction; +import gnu.javax.crypto.sasl.NoSuchUserException; +import gnu.javax.crypto.sasl.UserAlreadyExistsException; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.security.AccessController; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; + +/** + * A representation of a Plain password file. + */ +public class PasswordFile +{ + private static String DEFAULT_FILE; + static + { + DEFAULT_FILE = (String) AccessController.doPrivileged + (new GetPropertyAction(PlainRegistry.PASSWORD_FILE, + PlainRegistry.DEFAULT_PASSWORD_FILE)); + } + private Hashtable entries; + private File passwdFile; + private long lastmod; + + public PasswordFile() throws IOException + { + this(DEFAULT_FILE); + } + + public PasswordFile(File pwFile) throws IOException + { + this(pwFile.getAbsolutePath()); + } + + public PasswordFile(String fileName) throws IOException + { + passwdFile = new File(fileName); + update(); + } + + public synchronized void add(String user, String passwd, String[] attributes) + throws IOException + { + checkCurrent(); + if (entries.containsKey(user)) + throw new UserAlreadyExistsException(user); + if (attributes.length != 5) + throw new IllegalArgumentException("Wrong number of attributes"); + // create the new entry + String[] fields = new String[7]; + fields[0] = user; + fields[1] = passwd; + System.arraycopy(attributes, 0, fields, 2, 5); + entries.put(user, fields); + savePasswd(); + } + + public synchronized void changePasswd(String user, String passwd) + throws IOException + { + checkCurrent(); + if (! entries.containsKey(user)) + throw new NoSuchUserException(user); + String[] fields = (String[]) entries.get(user); // get the existing entry + fields[1] = passwd; // modify the password field + entries.remove(user); // delete the existing entry + entries.put(user, fields); // add the new entry + savePasswd(); + } + + public synchronized String[] lookup(String user) throws IOException + { + checkCurrent(); + if (! entries.containsKey(user)) + throw new NoSuchUserException(user); + return (String[]) entries.get(user); + } + + public synchronized boolean contains(String s) throws IOException + { + checkCurrent(); + return entries.containsKey(s); + } + + private synchronized void update() throws IOException + { + lastmod = passwdFile.lastModified(); + readPasswd(new FileInputStream(passwdFile)); + } + + private void checkCurrent() throws IOException + { + if (passwdFile.lastModified() > lastmod) + update(); + } + + private synchronized void readPasswd(InputStream in) throws IOException + { + BufferedReader din = new BufferedReader(new InputStreamReader(in)); + String line; + entries = new Hashtable(); + String[] fields = new String[7]; + while ((line = din.readLine()) != null) + { + StringTokenizer st = new StringTokenizer(line, ":", true); + try + { + fields[0] = st.nextToken(); // username + st.nextToken(); + fields[1] = st.nextToken(); // passwd + if (fields[1].equals(":")) + fields[1] = ""; + else + st.nextToken(); + fields[2] = st.nextToken(); // uid + if (fields[2].equals(":")) + fields[2] = ""; + else + st.nextToken(); + fields[3] = st.nextToken(); // gid + if (fields[3].equals(":")) + fields[3] = ""; + else + st.nextToken(); + fields[4] = st.nextToken(); // gecos + if (fields[4].equals(":")) + fields[4] = ""; + else + st.nextToken(); + fields[5] = st.nextToken(); // dir + if (fields[5].equals(":")) + fields[5] = ""; + else + st.nextToken(); + fields[6] = st.nextToken(); // shell + if (fields[6].equals(":")) + fields[6] = ""; + } + catch (NoSuchElementException ignored) + { + continue; + } + entries.put(fields[0], fields); + } + } + + private synchronized void savePasswd() throws IOException + { + if (passwdFile != null) + { + FileOutputStream fos = new FileOutputStream(passwdFile); + PrintWriter pw = null; + try + { + pw = new PrintWriter(fos); + String key; + String[] fields; + CPStringBuilder sb; + Enumeration keys = entries.keys(); + while (keys.hasMoreElements()) + { + key = (String) keys.nextElement(); + fields = (String[]) entries.get(key); + sb = new CPStringBuilder(fields[0]); + for (int i = 1; i < fields.length; i++) + sb.append(":" + fields[i]); + pw.println(sb.toString()); + } + } + finally + { + if (pw != null) + try + { + pw.flush(); + } + finally + { + pw.close(); + } + if (fos != null) + try + { + fos.close(); + } + catch (IOException ignored) + { + } + lastmod = passwdFile.lastModified(); + } + } + } +} diff --git a/libjava/classpath/gnu/javax/crypto/sasl/plain/PlainAuthInfoProvider.java b/libjava/classpath/gnu/javax/crypto/sasl/plain/PlainAuthInfoProvider.java new file mode 100644 index 000000000..5f35c455b --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/plain/PlainAuthInfoProvider.java @@ -0,0 +1,166 @@ +/* PlainAuthInfoProvider.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.plain; + +import gnu.java.security.Registry; +import gnu.javax.crypto.sasl.IAuthInfoProvider; +import gnu.javax.crypto.sasl.NoSuchUserException; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.security.sasl.AuthenticationException; + +/** + * The PLAIN mechanism authentication information provider implementation. + */ +public class PlainAuthInfoProvider + implements IAuthInfoProvider, PlainRegistry +{ + private PasswordFile passwordFile = null; + + // implicit 0-args constrcutor + + public void activate(Map context) throws AuthenticationException + { + try + { + if (context == null) + passwordFile = new PasswordFile(); + else + { + String pfn = (String) context.get(PASSWORD_FILE); + if (pfn == null) + passwordFile = new PasswordFile(); + else + passwordFile = new PasswordFile(pfn); + } + } + catch (IOException x) + { + throw new AuthenticationException("activate()", x); + } + } + + public void passivate() throws AuthenticationException + { + passwordFile = null; + } + + public boolean contains(String userName) throws AuthenticationException + { + if (passwordFile == null) + throw new AuthenticationException("contains()", + new IllegalStateException()); + boolean result = false; + try + { + result = passwordFile.contains(userName); + } + catch (IOException x) + { + throw new AuthenticationException("contains()", x); + } + return result; + } + + public Map lookup(Map userID) throws AuthenticationException + { + if (passwordFile == null) + throw new AuthenticationException("lookup()", new IllegalStateException()); + Map result = new HashMap(); + try + { + String userName = (String) userID.get(Registry.SASL_USERNAME); + if (userName == null) + throw new NoSuchUserException(""); + String[] data = passwordFile.lookup(userName); + result.put(Registry.SASL_USERNAME, data[0]); + result.put(Registry.SASL_PASSWORD, data[1]); + result.put(UID_FIELD, data[2]); + result.put(GID_FIELD, data[3]); + result.put(GECOS_FIELD, data[4]); + result.put(DIR_FIELD, data[5]); + result.put(SHELL_FIELD, data[6]); + } + catch (Exception x) + { + if (x instanceof AuthenticationException) + throw (AuthenticationException) x; + throw new AuthenticationException("lookup()", x); + } + return result; + } + + public void update(Map userCredentials) throws AuthenticationException + { + if (passwordFile == null) + throw new AuthenticationException("update()", new IllegalStateException()); + try + { + String userName = (String) userCredentials.get(Registry.SASL_USERNAME); + String password = (String) userCredentials.get(Registry.SASL_PASSWORD); + String uid = (String) userCredentials.get(UID_FIELD); + String gid = (String) userCredentials.get(GID_FIELD); + String gecos = (String) userCredentials.get(GECOS_FIELD); + String dir = (String) userCredentials.get(DIR_FIELD); + String shell = (String) userCredentials.get(SHELL_FIELD); + if (uid == null || gid == null || gecos == null || dir == null + || shell == null) + passwordFile.changePasswd(userName, password); + else + { + String[] attributes = new String[] { uid, gid, gecos, dir, shell }; + passwordFile.add(userName, password, attributes); + } + } + catch (Exception x) + { + if (x instanceof AuthenticationException) + throw (AuthenticationException) x; + throw new AuthenticationException("update()", x); + } + } + + public Map getConfiguration(String mode) throws AuthenticationException + { + throw new AuthenticationException("", new UnsupportedOperationException()); + } +} diff --git a/libjava/classpath/gnu/javax/crypto/sasl/plain/PlainClient.java b/libjava/classpath/gnu/javax/crypto/sasl/plain/PlainClient.java new file mode 100644 index 000000000..f984ed13f --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/plain/PlainClient.java @@ -0,0 +1,156 @@ +/* PlainClient.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.plain; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.security.Registry; +import gnu.javax.crypto.sasl.ClientMechanism; + +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; + +/** + * The PLAIN SASL client-side mechanism. + */ +public class PlainClient + extends ClientMechanism + implements SaslClient +{ + public PlainClient() + { + super(Registry.SASL_PLAIN_MECHANISM); + } + + protected void initMechanism() throws SaslException + { + } + + protected void resetMechanism() throws SaslException + { + } + + public boolean hasInitialResponse() + { + return true; + } + + public byte[] evaluateChallenge(final byte[] challenge) throws SaslException + { + try + { + final String username; + final char[] password; + Callback[] callbacks; + if ((! properties.containsKey(Registry.SASL_USERNAME)) + && (! properties.containsKey(Registry.SASL_PASSWORD))) + { + callbacks = new Callback[2]; + final NameCallback nameCB; + final String defaultName = System.getProperty("user.name"); + if (defaultName == null) + nameCB = new NameCallback("username: "); + else + nameCB = new NameCallback("username: ", defaultName); + final PasswordCallback pwdCB = new PasswordCallback("password: ", + false); + callbacks[0] = nameCB; + callbacks[1] = pwdCB; + this.handler.handle(callbacks); + username = nameCB.getName(); + password = pwdCB.getPassword(); + } + else + { + if (properties.containsKey(Registry.SASL_USERNAME)) + username = (String) properties.get(Registry.SASL_USERNAME); + else + { + callbacks = new Callback[1]; + final NameCallback nameCB; + final String defaultName = System.getProperty("user.name"); + if (defaultName == null) + nameCB = new NameCallback("username: "); + else + nameCB = new NameCallback("username: ", defaultName); + callbacks[0] = nameCB; + this.handler.handle(callbacks); + username = nameCB.getName(); + } + if (properties.containsKey(Registry.SASL_PASSWORD)) + password = ((String) properties.get(Registry.SASL_PASSWORD)).toCharArray(); + else + { + callbacks = new Callback[1]; + final PasswordCallback pwdCB = new PasswordCallback("password: ", + false); + callbacks[0] = pwdCB; + this.handler.handle(callbacks); + password = pwdCB.getPassword(); + } + } + if (password == null) + throw new SaslException("null password supplied"); + final CPStringBuilder sb = new CPStringBuilder(); + if (authorizationID != null) + sb.append(authorizationID); + sb.append('\0'); + sb.append(username); + sb.append('\0'); + sb.append(password); + this.complete = true; + final byte[] response = sb.toString().getBytes("UTF-8"); + return response; + } + catch (Exception x) + { + if (x instanceof SaslException) + throw (SaslException) x; + throw new SaslException("evaluateChallenge()", x); + } + } + + protected String getNegotiatedQOP() + { + return Registry.QOP_AUTH; + } +} diff --git a/libjava/classpath/gnu/javax/crypto/sasl/plain/PlainRegistry.java b/libjava/classpath/gnu/javax/crypto/sasl/plain/PlainRegistry.java new file mode 100644 index 000000000..68b121d96 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/plain/PlainRegistry.java @@ -0,0 +1,57 @@ +/* PlainRegistry.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.plain; + +public interface PlainRegistry +{ + /** Name of PLAIN password file property. */ + String PASSWORD_FILE = "gnu.crypto.sasl.plain.password.file"; + /** Default fully qualified pathname of the PLAIN password file. */ + String DEFAULT_PASSWORD_FILE = "/etc/tpasswd"; + /** Name of the UID field in the plain password file. */ + String UID_FIELD = "plain.uid"; + /** Name of the GID field in the plain password file. */ + String GID_FIELD = "plain.gid"; + /** Name of the GECOS field in the plain password file. */ + String GECOS_FIELD = "plain.gecos"; + /** Name of the DIR field in the plain password file. */ + String DIR_FIELD = "plain.dir"; + /** Name of the SHELL field in the plain password file. */ + String SHELL_FIELD = "plain.shell"; +} diff --git a/libjava/classpath/gnu/javax/crypto/sasl/plain/PlainServer.java b/libjava/classpath/gnu/javax/crypto/sasl/plain/PlainServer.java new file mode 100644 index 000000000..9d97bc029 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/plain/PlainServer.java @@ -0,0 +1,155 @@ +/* PlainServer.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.sasl.plain; + +import gnu.java.security.Registry; +import gnu.javax.crypto.sasl.NoSuchUserException; +import gnu.javax.crypto.sasl.ServerMechanism; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; + +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +/** + * The PLAIN SASL server-side mechanism. + */ +public class PlainServer + extends ServerMechanism + implements SaslServer +{ + public PlainServer() + { + super(Registry.SASL_PLAIN_MECHANISM); + } + + protected void initMechanism() throws SaslException + { + } + + protected void resetMechanism() throws SaslException + { + } + + public byte[] evaluateResponse(final byte[] response) throws SaslException + { + if (response == null) + return null; + try + { + final String nullStr = new String("\0"); + final StringTokenizer strtok = new StringTokenizer(new String(response), + nullStr, true); + authorizationID = strtok.nextToken(); + if (! authorizationID.equals(nullStr)) + strtok.nextToken(); + else + authorizationID = null; + final String id = strtok.nextToken(); + if (id.equals(nullStr)) + throw new SaslException("No identity given"); + if (authorizationID == null) + authorizationID = id; + if ((! authorizationID.equals(nullStr)) && (! authorizationID.equals(id))) + throw new SaslException("Delegation not supported"); + strtok.nextToken(); + final byte[] pwd; + try + { + pwd = strtok.nextToken().getBytes("UTF-8"); + } + catch (UnsupportedEncodingException x) + { + throw new SaslException("evaluateResponse()", x); + } + if (pwd == null) + throw new SaslException("No password given"); + final byte[] password; + try + { + password = new String(lookupPassword(id)).getBytes("UTF-8"); + } + catch (UnsupportedEncodingException x) + { + throw new SaslException("evaluateResponse()", x); + } + if (! Arrays.equals(pwd, password)) + throw new SaslException("Password incorrect"); + this.complete = true; + return null; + } + catch (NoSuchElementException x) + { + throw new SaslException("evaluateResponse()", x); + } + } + + protected String getNegotiatedQOP() + { + return Registry.QOP_AUTH; + } + + private char[] lookupPassword(final String userName) throws SaslException + { + try + { + if (! authenticator.contains(userName)) + throw new NoSuchUserException(userName); + final Map userID = new HashMap(); + userID.put(Registry.SASL_USERNAME, userName); + final Map credentials = authenticator.lookup(userID); + final String password = (String) credentials.get(Registry.SASL_PASSWORD); + if (password == null) + throw new SaslException("lookupPassword()", new InternalError()); + return password.toCharArray(); + } + catch (IOException x) + { + if (x instanceof SaslException) + throw (SaslException) x; + throw new SaslException("lookupPassword()", x); + } + } +} |