View Javadoc
1   /**
2    * Waffle (https://github.com/dblock/waffle)
3    *
4    * Copyright (c) 2010 - 2015 Application Security, Inc.
5    *
6    * All rights reserved. This program and the accompanying materials
7    * are made available under the terms of the Eclipse Public License v1.0
8    * which accompanies this distribution, and is available at
9    * http://www.eclipse.org/legal/epl-v10.html
10   *
11   * Contributors:
12   *     Application Security, Inc.
13   */
14  package waffle.util;
15  
16  import javax.servlet.http.HttpServletRequest;
17  
18  import com.google.common.io.BaseEncoding;
19  
20  /**
21   * Authorization header.
22   * 
23   * @author dblock[at]dblock[dot]org
24   */
25  public class AuthorizationHeader {
26  
27      /** The request. */
28      private final HttpServletRequest request;
29  
30      /**
31       * Instantiates a new authorization header.
32       *
33       * @param httpServletRequest
34       *            the http servlet request
35       */
36      public AuthorizationHeader(final HttpServletRequest httpServletRequest) {
37          this.request = httpServletRequest;
38      }
39  
40      /**
41       * Gets the header.
42       *
43       * @return the header
44       */
45      public String getHeader() {
46          return this.request.getHeader("Authorization");
47      }
48  
49      /**
50       * Checks if is null.
51       *
52       * @return true, if is null
53       */
54      public boolean isNull() {
55          return this.getHeader() == null || this.getHeader().length() == 0;
56      }
57  
58      /**
59       * Returns a supported security package string.
60       * 
61       * @return Negotiate or NTLM.
62       */
63      public String getSecurityPackage() {
64          final String header = this.getHeader();
65  
66          if (header == null) {
67              throw new RuntimeException("Missing Authorization: header");
68          }
69  
70          final int space = header.indexOf(' ');
71          if (space > 0) {
72              return header.substring(0, space);
73          }
74  
75          throw new RuntimeException("Invalid Authorization header: " + header);
76      }
77  
78      /* (non-Javadoc)
79       * @see java.lang.Object#toString()
80       */
81      @Override
82      public String toString() {
83          return this.isNull() ? "<none>" : this.getHeader();
84      }
85  
86      /**
87       * Gets the token.
88       *
89       * @return the token
90       */
91      public String getToken() {
92          return this.getHeader().substring(this.getSecurityPackage().length() + 1);
93      }
94  
95      /**
96       * Gets the token bytes.
97       *
98       * @return the token bytes
99       */
100     public byte[] getTokenBytes() {
101         try {
102             return BaseEncoding.base64().decode(this.getToken());
103         } catch (final IllegalArgumentException e) {
104             throw new RuntimeException("Invalid authorization header.");
105         }
106     }
107 
108     /**
109      * Checks if is ntlm type1 message.
110      *
111      * @return true, if is ntlm type1 message
112      */
113     public boolean isNtlmType1Message() {
114         if (this.isNull()) {
115             return false;
116         }
117 
118         final byte[] tokenBytes = this.getTokenBytes();
119         if (!NtlmMessage.isNtlmMessage(tokenBytes)) {
120             return false;
121         }
122 
123         return 1 == NtlmMessage.getMessageType(tokenBytes);
124     }
125 
126     /**
127      * Checks if is SP nego message.
128      *
129      * @return true, if is SP nego message
130      */
131     public boolean isSPNegoMessage() {
132 
133         if (this.isNull()) {
134             return false;
135         }
136 
137         final byte[] tokenBytes = this.getTokenBytes();
138         if (!SPNegoMessage.isSPNegoMessage(tokenBytes)) {
139             return false;
140         }
141 
142         return true;
143     }
144 
145     /**
146      * When using NTLM authentication and the browser is making a POST request, it preemptively sends a Type 2
147      * authentication message (without the POSTed data). The server responds with a 401, and the browser sends a Type 3
148      * request with the POSTed data. This is to avoid the situation where user's credentials might be potentially
149      * invalid, and all this data is being POSTed across the wire.
150      * 
151      * @return True if request is an NTLM POST or PUT with an Authorization header and no data.
152      */
153     public boolean isNtlmType1PostAuthorizationHeader() {
154         if (!this.request.getMethod().equals("POST") && !this.request.getMethod().equals("PUT")) {
155             return false;
156         }
157 
158         if (this.request.getContentLength() != 0) {
159             return false;
160         }
161 
162         return this.isNtlmType1Message() || this.isSPNegoMessage();
163     }
164 }