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