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         StringBuilder buf = new StringBuilder();
126         byte[] b = s.getBytes();
127 
128         buf.append(__OBFUSCATE);
129         for (int i=0;i<b.length;i++)
130         {
131             byte b1 = b[i];
132             byte b2 = b[s.length()-(i+1)];
133             int i1= 127+b1+b2;
134             int i2= 127+b1-b2;
135             int i0=i1*256+i2;
136             String x=Integer.toString(i0,36);
137 
138             switch(x.length())
139             {
140                 case 1:buf.append('0');
141                 case 2:buf.append('0');
142                 case 3:buf.append('0');
143                 default:buf.append(x);
144             }
145         }
146         return buf.toString();
147 
148     }
149     
150     /* ------------------------------------------------------------ */
151     public static String deobfuscate(String s)
152     {
153         if (s.startsWith(__OBFUSCATE))
154             s=s.substring(4);
155         
156         byte[] b=new byte[s.length()/2];
157         int l=0;
158         for (int i=0;i<s.length();i+=4)
159         {
160             String x=s.substring(i,i+4);
161             int i0 = Integer.parseInt(x,36);
162             int i1=(i0/256);
163             int i2=(i0%256);
164             b[l++]=(byte)((i1+i2-254)/2);
165         }
166 
167         return new String(b,0,l);
168     }
169 
170     /* ------------------------------------------------------------ */
171     /** Get a password.
172      * A password is obtained by trying <UL>
173      * <LI>Calling <Code>System.getProperty(realm,dft)</Code>
174      * <LI>Prompting for a password
175      * <LI>Using promptDft if nothing was entered.
176      * </UL>
177      * @param realm The realm name for the password, used as a SystemProperty name.
178      * @param dft The default password.
179      * @param promptDft The default to use if prompting for the password.
180      * @return Password
181      */
182     public static Password getPassword(String realm,String dft, String promptDft)
183     {
184         String passwd=System.getProperty(realm,dft);
185         if (passwd==null || passwd.length()==0)
186         {
187             try
188             {
189                 System.out.print(realm+
190                                  ((promptDft!=null && promptDft.length()>0)
191                                   ?" [dft]":"")+" : ");
192                 System.out.flush();
193                 byte[] buf = new byte[512];
194                 int len=System.in.read(buf);
195                 if (len>0)
196                     passwd=new String(buf,0,len).trim();
197             }
198             catch(IOException e)
199             {
200                 Log.warn(Log.EXCEPTION,e);
201             }
202             if (passwd==null || passwd.length()==0)
203                 passwd=promptDft;
204         }
205         return new Password(passwd);
206     }
207     
208     
209     /* ------------------------------------------------------------ */
210     /** 
211      * @param arg 
212      */
213     public static void main(String[] arg)
214     {
215         if (arg.length!=1 && arg.length!=2 )
216         {
217             System.err.println("Usage - java org.mortbay.jetty.security.Password [<user>] <password>");
218             System.err.println("If the password is ?, the user will be prompted for the password");
219             System.exit(1);
220         }
221         String p=arg[arg.length==1?0:1];
222         Password pw = "?".equals(p)?new Password(p):new Password(p);
223         System.err.println(pw.toString());
224         System.err.println(obfuscate(pw.toString()));
225         System.err.println(Credential.MD5.digest(p));
226         if (arg.length==2)
227             System.err.println(Credential.Crypt.crypt(arg[0],pw.toString()));
228     }    
229 }
230 
231