Merge with 2ad3c504ee5d73982c0ef23336276dc1fc9e165f
[pithos] / src / gr / ebs / gss / server / webdav / milton / GssNonceProvider.java
1 /*
2  * Copyright 2011 Electronic Business Systems Ltd.
3  *
4  * This file is part of GSS.
5  *
6  * GSS is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GSS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with GSS.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 package gr.ebs.gss.server.webdav.milton;
20
21 import static gr.ebs.gss.server.configuration.GSSConfigurationFactory.getConfiguration;
22 import gr.ebs.gss.client.exceptions.RpcException;
23 import gr.ebs.gss.server.domain.WebDavNonce;
24 import gr.ebs.gss.server.ejb.ExternalAPI;
25
26 import java.util.Date;
27 import java.util.Map;
28 import java.util.UUID;
29 import java.util.concurrent.ConcurrentHashMap;
30
31 import javax.naming.Context;
32 import javax.naming.InitialContext;
33 import javax.naming.NamingException;
34 import javax.rmi.PortableRemoteObject;
35
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 import com.bradmcevoy.http.Request;
40 import com.bradmcevoy.http.Resource;
41 import com.bradmcevoy.http.http11.auth.ExpiredNonceRemover;
42 import com.bradmcevoy.http.http11.auth.Nonce;
43 import com.bradmcevoy.http.http11.auth.NonceProvider;
44 import com.bradmcevoy.http.http11.auth.SimpleMemoryNonceProvider;
45 import com.bradmcevoy.http.http11.auth.NonceProvider.NonceValidity;
46
47
48 /**
49  * @author kman
50  *
51  */
52 public class GssNonceProvider implements NonceProvider {
53
54     private static final Logger log = LoggerFactory.getLogger( GssNonceProvider.class );
55     private final int nonceValiditySeconds;
56     private boolean enableNonceCountChecking;
57     public GssNonceProvider( int nonceValiditySeconds ) {
58         this.nonceValiditySeconds = nonceValiditySeconds;
59     }
60     
61         @Override
62         public String createNonce(Resource resource, Request request ) {
63                 UUID id = UUID.randomUUID();
64         Date now = new Date();
65         Nonce n = new Nonce( id, now );
66         createOrUpdateGssNonce(n);
67         return n.getValue().toString();
68         }
69
70         @Override
71         public NonceValidity getNonceValidity( String nonce, Long nc ) {
72         log.trace( "getNonceValidity: " + nonce );
73         UUID value = null;
74         try {
75             value = UUID.fromString( nonce );
76         } catch( Exception e ) {
77             log.warn( "couldnt parse nonce" );
78             return NonceValidity.INVALID;
79         }
80         Nonce n = getNonce( nonce );
81         if( n == null ) {
82             log.debug( "not found");
83             return NonceValidity.INVALID;
84         } else {
85             if( isExpired( n.getIssued() ) ) {
86                 log.debug( "nonce has expired" );
87                 return NonceValidity.EXPIRED;
88             } else {
89                 if( nc == null ) {
90                     log.trace( "nonce ok" );
91                     return NonceValidity.OK;
92                 } else {
93                     if( enableNonceCountChecking && nc <= n.getNonceCount() ) {
94                         log.warn( "nonce-count was not greater then previous, possible replay attack. new: " + nc + " old:" + n.getNonceCount() );
95                         return NonceValidity.INVALID;
96                     } else {
97                         log.trace( "nonce and nonce-count ok" );
98                         Nonce newNonce = n.increaseNonceCount( nc );
99                         createOrUpdateGssNonce(newNonce);
100                         return NonceValidity.OK;
101                     }
102                 }
103             }
104         }
105     }
106
107     private boolean isExpired( Date issued ) {
108         long dif = ( System.currentTimeMillis() - issued.getTime() ) / 1000;
109         return dif > nonceValiditySeconds;
110     }
111     
112     private void createOrUpdateGssNonce(Nonce nonce){
113         try{
114                 WebDavNonce non = getService().getWebDavNonce(nonce.getValue().toString());
115                 if(non==null){
116                         non = new WebDavNonce();
117                         non.setId(nonce.getValue().toString());
118                 }
119                 non.setIssued(nonce.getIssued());
120                 non.setNonceCount(nonce.getNonceCount());
121                 getService().saveOrUpdateWebDavNonce(non);
122         }
123         catch(Exception ex){
124                 throw new RuntimeException("Unable to save or update nonce",ex);
125         }
126     }
127     
128     private Nonce getNonce(String id){
129         try{
130                 WebDavNonce non = getService().getWebDavNonce(id);
131                 if(non!=null){
132                         Nonce nonce = new Nonce(UUID.fromString(id), non.getIssued());
133                         nonce.increaseNonceCount(non.getNonceCount());
134                         return nonce;
135                 }
136         }
137         catch(Exception ex){
138                 throw new RuntimeException("Unable to retrieve nonce",ex);
139         }
140         return null;
141     }
142     
143     /**
144          * A helper method that retrieves a reference to the ExternalAPI bean and
145          * stores it for future use.
146          *
147          * @return an ExternalAPI instance
148          * @throws RpcException in case an error occurs
149          */
150         protected ExternalAPI getService() throws RpcException {
151                 try {
152                         final Context ctx = new InitialContext();
153                         final Object ref = ctx.lookup(getConfiguration().getString("externalApiPath"));
154                         return (ExternalAPI) PortableRemoteObject.narrow(ref, ExternalAPI.class);
155                 } catch (final NamingException e) {
156                         log.error("Unable to retrieve the ExternalAPI EJB", e);
157                         throw new RpcException("An error occurred while contacting the naming service");
158                 }
159         }
160         /**
161      * IE seems to send nc (nonce count) parameters out of order. To correctly
162      * implement checking we need to record which nonces have been sent, and not
163      * assume they will be sent in a monotonically increasing sequence.
164      *
165      * The quick fix here is to disable checking of the nc param, since other
166      * common servers seem to do so to.
167      *
168      * Note that this will allow replay attacks.
169      *
170      * @return
171      */
172     public boolean isEnableNonceCountChecking() {
173         return enableNonceCountChecking;
174     }
175
176    public void setEnableNonceCountChecking( boolean enableNonceCountChecking ) {
177        this.enableNonceCountChecking = enableNonceCountChecking;
178    }
179 }