1//
2//  ========================================================================
3//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4//  ------------------------------------------------------------------------
5//  All rights reserved. This program and the accompanying materials
6//  are made available under the terms of the Eclipse Public License v1.0
7//  and Apache License v2.0 which accompanies this distribution.
8//
9//      The Eclipse Public License is available at
10//      http://www.eclipse.org/legal/epl-v10.html
11//
12//      The Apache License v2.0 is available at
13//      http://www.opensource.org/licenses/apache2.0.php
14//
15//  You may elect to redistribute this code under either of these licenses.
16//  ========================================================================
17//
18
19package org.eclipse.jetty.util.security;
20
21import java.io.IOException;
22import java.util.Arrays;
23
24import org.eclipse.jetty.util.log.Log;
25import org.eclipse.jetty.util.log.Logger;
26
27/* ------------------------------------------------------------ */
28/**
29 * Password utility class.
30 *
31 * This utility class gets a password or pass phrase either by:
32 *
33 * <PRE>
34 *  + Password is set as a system property.
35 *  + The password is prompted for and read from standard input
36 *  + A program is run to get the password.
37 * </pre>
38 *
39 * Passwords that begin with OBF: are de obfuscated. Passwords can be obfuscated
40 * by run org.eclipse.util.Password as a main class. Obfuscated password are
41 * required if a system needs to recover the full password (eg. so that it may
42 * be passed to another system). They are not secure, but prevent casual
43 * observation.
44 * <p>
45 * Passwords that begin with CRYPT: are oneway encrypted with UnixCrypt. The
46 * real password cannot be retrieved, but comparisons can be made to other
47 * passwords. A Crypt can be generated by running org.eclipse.util.UnixCrypt as
48 * a main class, passing password and then the username. Checksum passwords are
49 * a secure(ish) way to store passwords that only need to be checked rather than
50 * recovered. Note that it is not strong security - specially if simple
51 * passwords are used.
52 *
53 *
54 */
55public class Password extends Credential
56{
57    private static final Logger LOG = Log.getLogger(Password.class);
58
59    private static final long serialVersionUID = 5062906681431569445L;
60
61    public static final String __OBFUSCATE = "OBF:";
62
63    private String _pw;
64
65    /* ------------------------------------------------------------ */
66    /**
67     * Constructor.
68     *
69     * @param password The String password.
70     */
71    public Password(String password)
72    {
73        _pw = password;
74
75        // expand password
76        while (_pw != null && _pw.startsWith(__OBFUSCATE))
77            _pw = deobfuscate(_pw);
78    }
79
80    /* ------------------------------------------------------------ */
81    @Override
82    public String toString()
83    {
84        return _pw;
85    }
86
87    /* ------------------------------------------------------------ */
88    public String toStarString()
89    {
90        return "*****************************************************".substring(0, _pw.length());
91    }
92
93    /* ------------------------------------------------------------ */
94    @Override
95    public boolean check(Object credentials)
96    {
97        if (this == credentials) return true;
98
99        if (credentials instanceof Password) return credentials.equals(_pw);
100
101        if (credentials instanceof String) return credentials.equals(_pw);
102
103        if (credentials instanceof char[]) return Arrays.equals(_pw.toCharArray(), (char[]) credentials);
104
105        if (credentials instanceof Credential) return ((Credential) credentials).check(_pw);
106
107        return false;
108    }
109
110    /* ------------------------------------------------------------ */
111    @Override
112    public boolean equals(Object o)
113    {
114        if (this == o)
115            return true;
116
117        if (null == o)
118            return false;
119
120        if (o instanceof Password)
121        {
122            Password p = (Password) o;
123            //noinspection StringEquality
124            return p._pw == _pw || (null != _pw && _pw.equals(p._pw));
125        }
126
127        if (o instanceof String)
128            return o.equals(_pw);
129
130        return false;
131    }
132
133    /* ------------------------------------------------------------ */
134    @Override
135    public int hashCode()
136    {
137        return null == _pw ? super.hashCode() : _pw.hashCode();
138    }
139
140    /* ------------------------------------------------------------ */
141    public static String obfuscate(String s)
142    {
143        StringBuilder buf = new StringBuilder();
144        byte[] b = s.getBytes();
145
146        buf.append(__OBFUSCATE);
147        for (int i = 0; i < b.length; i++)
148        {
149            byte b1 = b[i];
150            byte b2 = b[s.length() - (i + 1)];
151            int i1 = 127 + b1 + b2;
152            int i2 = 127 + b1 - b2;
153            int i0 = i1 * 256 + i2;
154            String x = Integer.toString(i0, 36);
155
156            switch (x.length())
157            {
158                case 1:
159                    buf.append('0');
160                    buf.append('0');
161                    buf.append('0');
162                    buf.append(x);
163                    break;
164                case 2:
165                    buf.append('0');
166                    buf.append('0');
167                    buf.append(x);
168                    break;
169                case 3:
170                    buf.append('0');
171                    buf.append(x);
172                    break;
173                default:
174                    buf.append(x);
175                    break;
176            }
177        }
178        return buf.toString();
179
180    }
181
182    /* ------------------------------------------------------------ */
183    public static String deobfuscate(String s)
184    {
185        if (s.startsWith(__OBFUSCATE)) s = s.substring(4);
186
187        byte[] b = new byte[s.length() / 2];
188        int l = 0;
189        for (int i = 0; i < s.length(); i += 4)
190        {
191            String x = s.substring(i, i + 4);
192            int i0 = Integer.parseInt(x, 36);
193            int i1 = (i0 / 256);
194            int i2 = (i0 % 256);
195            b[l++] = (byte) ((i1 + i2 - 254) / 2);
196        }
197
198        return new String(b, 0, l);
199    }
200
201    /* ------------------------------------------------------------ */
202    /**
203     * Get a password. A password is obtained by trying
204     * <UL>
205     * <LI>Calling <Code>System.getProperty(realm,dft)</Code>
206     * <LI>Prompting for a password
207     * <LI>Using promptDft if nothing was entered.
208     * </UL>
209     *
210     * @param realm The realm name for the password, used as a SystemProperty
211     *                name.
212     * @param dft The default password.
213     * @param promptDft The default to use if prompting for the password.
214     * @return Password
215     */
216    public static Password getPassword(String realm, String dft, String promptDft)
217    {
218        String passwd = System.getProperty(realm, dft);
219        if (passwd == null || passwd.length() == 0)
220        {
221            try
222            {
223                System.out.print(realm + ((promptDft != null && promptDft.length() > 0) ? " [dft]" : "") + " : ");
224                System.out.flush();
225                byte[] buf = new byte[512];
226                int len = System.in.read(buf);
227                if (len > 0) passwd = new String(buf, 0, len).trim();
228            }
229            catch (IOException e)
230            {
231                LOG.warn(Log.EXCEPTION, e);
232            }
233            if (passwd == null || passwd.length() == 0) passwd = promptDft;
234        }
235        return new Password(passwd);
236    }
237
238    /* ------------------------------------------------------------ */
239    /**
240     * @param arg
241     */
242    public static void main(String[] arg)
243    {
244        if (arg.length != 1 && arg.length != 2)
245        {
246            System.err.println("Usage - java org.eclipse.jetty.security.Password [<user>] <password>");
247            System.err.println("If the password is ?, the user will be prompted for the password");
248            System.exit(1);
249        }
250        String p = arg[arg.length == 1 ? 0 : 1];
251        Password pw = new Password(p);
252        System.err.println(pw.toString());
253        System.err.println(obfuscate(pw.toString()));
254        System.err.println(Credential.MD5.digest(p));
255        if (arg.length == 2) System.err.println(Credential.Crypt.crypt(arg[0], pw.toString()));
256    }
257}
258