PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/org/jgroups/protocols/AUTH.java

https://github.com/rachmatowicz/JGroups
Java | 277 lines | 197 code | 41 blank | 39 comment | 43 complexity | 5a2b0bc65364c051a065087d7164491b MD5 | raw file
  1. package org.jgroups.protocols;
  2. import org.jgroups.*;
  3. import org.jgroups.annotations.MBean;
  4. import org.jgroups.annotations.Property;
  5. import org.jgroups.annotations.XmlAttribute;
  6. import org.jgroups.auth.AuthToken;
  7. import org.jgroups.auth.X509Token;
  8. import org.jgroups.conf.ClassConfigurator;
  9. import org.jgroups.protocols.pbcast.GMS;
  10. import org.jgroups.protocols.pbcast.JoinRsp;
  11. import org.jgroups.stack.Protocol;
  12. import org.jgroups.util.MessageBatch;
  13. import java.util.ArrayList;
  14. import java.util.LinkedList;
  15. import java.util.List;
  16. /**
  17. * The AUTH protocol adds a layer of authentication to JGroups. It intercepts join and merge requests and rejects them
  18. * if the joiner or merger is not permitted to join a or merge into a cluster. AUTH should be placed right below
  19. * {@link GMS} in the configuration.
  20. * @author Chris Mills
  21. * @author Bela Ban
  22. */
  23. @XmlAttribute(attrs={
  24. "auth_value", // SimpleToken, MD5Token, X509Token
  25. "fixed_members_value", "fixed_members_seperator", // FixedMembershipToken
  26. "block_time", // DemoToken
  27. "client_principal_name", "client_password", "service_principal_name", // Krb5Token
  28. "token_hash", // MD5Token
  29. "match_string", "match_ip_address", "match_logical_name", // RegexMembership
  30. "keystore_type", "cert_alias", "keystore_path", "cipher_type",
  31. "cert_password", "keystore_password" // X509Token
  32. })
  33. @MBean(description="Provides authentication of joiners, to prevent un-authorized joining of a cluster")
  34. public class AUTH extends Protocol {
  35. /** Interface to provide callbacks for handling up events */
  36. public interface UpHandler {
  37. /**
  38. * Called when a message has been received
  39. * @param msg the message
  40. * @return true if the message should be passed up, else false
  41. */
  42. boolean handleUpMessage(Message msg);
  43. }
  44. /** Used on the coordinator to authentication joining member requests against */
  45. protected AuthToken auth_token;
  46. protected static final short GMS_ID=ClassConfigurator.getProtocolId(GMS.class);
  47. /** List of UpHandler which are called when an up event has been received. Usually used by AuthToken impls */
  48. protected final List<UpHandler> up_handlers=new ArrayList<>();
  49. protected Address local_addr;
  50. public AUTH() {name="AUTH";}
  51. protected volatile boolean authenticate_coord=true;
  52. @Property(description="Do join or merge responses from the coordinator also need to be authenticated")
  53. public AUTH setAuthCoord( boolean authenticateCoord) {
  54. this.authenticate_coord= authenticateCoord; return this;
  55. }
  56. @Property(name="auth_class",description="The fully qualified name of the class implementing the AuthToken interface")
  57. public void setAuthClass(String class_name) throws Exception {
  58. Object obj=Class.forName(class_name).newInstance();
  59. auth_token=(AuthToken)obj;
  60. auth_token.setAuth(this);
  61. }
  62. public String getAuthClass() {return auth_token != null? auth_token.getClass().getName() : null;}
  63. public AuthToken getAuthToken() {return auth_token;}
  64. public AUTH setAuthToken(AuthToken token) {this.auth_token=token; return this;}
  65. public AUTH register(UpHandler handler) {up_handlers.add(handler); return this;}
  66. public AUTH unregister(UpHandler handler) {up_handlers.remove(handler);return this;}
  67. public Address getAddress() {return local_addr;}
  68. public PhysicalAddress getPhysicalAddress() {return getTransport().getPhysicalAddress();}
  69. protected List<Object> getConfigurableObjects() {
  70. List<Object> retval=new LinkedList<>();
  71. if(auth_token != null)
  72. retval.add(auth_token);
  73. return retval;
  74. }
  75. public void init() throws Exception {
  76. super.init();
  77. if(auth_token == null)
  78. throw new IllegalStateException("no authentication mechanism configured");
  79. if(auth_token instanceof X509Token) {
  80. X509Token tmp=(X509Token)auth_token;
  81. tmp.setCertificate();
  82. }
  83. auth_token.init();
  84. }
  85. public void start() throws Exception {
  86. super.start();
  87. if(auth_token != null)
  88. auth_token.start();
  89. }
  90. public void stop() {
  91. if(auth_token != null)
  92. auth_token.stop();
  93. super.stop();
  94. }
  95. public void destroy() {
  96. if(auth_token != null)
  97. auth_token.destroy();
  98. super.destroy();
  99. }
  100. /**
  101. * An event was received from the layer below. Usually the current layer will want to examine the event type and
  102. * - depending on its type - perform some computation (e.g. removing headers from a MSG event type, or updating
  103. * the internal membership list when receiving a VIEW_CHANGE event).
  104. * Finally the event is either a) discarded, or b) an event is sent down the stack using {@code down_prot.down()}
  105. * or c) the event (or another event) is sent up the stack using {@code up_prot.up()}.
  106. */
  107. public Object up(Message msg) {
  108. // If we have a join or merge request --> authenticate, else pass up
  109. GMS.GmsHeader gms_hdr=getGMSHeader(msg);
  110. if(gms_hdr != null && needsAuthentication(gms_hdr)) {
  111. AuthHeader auth_hdr=msg.getHeader(id);
  112. if(auth_hdr == null)
  113. throw new IllegalStateException(String.format("found %s from %s but no AUTH header", gms_hdr, msg.src()));
  114. if(!handleAuthHeader(gms_hdr, auth_hdr, msg)) // authentication failed
  115. return null; // don't pass up
  116. }
  117. if(!callUpHandlers(msg))
  118. return null;
  119. return up_prot.up(msg);
  120. }
  121. public void up(MessageBatch batch) {
  122. for(Message msg: batch) {
  123. // If we have a join or merge request --> authenticate, else pass up
  124. GMS.GmsHeader gms_hdr=getGMSHeader(msg);
  125. if(gms_hdr != null && needsAuthentication(gms_hdr)) {
  126. AuthHeader auth_hdr=msg.getHeader(id);
  127. if(auth_hdr == null) {
  128. log.warn("%s: found GMS join or merge request from %s but no AUTH header", local_addr, batch.sender());
  129. sendRejectionMessage(gms_hdr.getType(), batch.sender(), "join or merge without an AUTH header");
  130. batch.remove(msg);
  131. }
  132. else if(!handleAuthHeader(gms_hdr, auth_hdr, msg)) // authentication failed
  133. batch.remove(msg); // don't pass up
  134. }
  135. }
  136. if(!batch.isEmpty())
  137. up_prot.up(batch);
  138. }
  139. /**
  140. * An event is to be sent down the stack. The layer may want to examine its type and perform
  141. * some action on it, depending on the event's type. If the event is a message MSG, then
  142. * the layer may need to add a header to it (or do nothing at all) before sending it down
  143. * the stack using {@code down_prot.down()}. In case of a GET_ADDRESS event (which tries to
  144. * retrieve the stack's address from one of the bottom layers), the layer may need to send
  145. * a new response event back up the stack using {@code up_prot.up()}.
  146. */
  147. public Object down(Event evt) {
  148. if(evt.getType() == Event.SET_LOCAL_ADDRESS)
  149. local_addr=evt.getArg();
  150. return down_prot.down(evt);
  151. }
  152. public Object down(Message msg) {
  153. GMS.GmsHeader hdr = getGMSHeader(msg);
  154. if(hdr != null && needsAuthentication(hdr)) {
  155. // we found a join request message - now add an AUTH Header
  156. msg.putHeader(this.id, new AuthHeader(this.auth_token));
  157. }
  158. return down_prot.down(msg);
  159. }
  160. protected boolean needsAuthentication(GMS.GmsHeader hdr) {
  161. switch(hdr.getType()) {
  162. case GMS.GmsHeader.JOIN_REQ:
  163. case GMS.GmsHeader.JOIN_REQ_WITH_STATE_TRANSFER:
  164. case GMS.GmsHeader.MERGE_REQ:
  165. return true;
  166. case GMS.GmsHeader.JOIN_RSP:
  167. case GMS.GmsHeader.MERGE_RSP:
  168. case GMS.GmsHeader.INSTALL_MERGE_VIEW:
  169. return this.authenticate_coord;
  170. default:
  171. return false;
  172. }
  173. }
  174. /**
  175. * Handles a GMS header
  176. * @param gms_hdr
  177. * @param msg
  178. * @return true if the message should be passed up, or else false
  179. */
  180. protected boolean handleAuthHeader(GMS.GmsHeader gms_hdr, AuthHeader auth_hdr, Message msg) {
  181. if(needsAuthentication(gms_hdr)) {
  182. if(this.auth_token.authenticate(auth_hdr.getToken(), msg))
  183. return true; // authentication passed, send message up the stack
  184. else {
  185. log.warn("%s: failed to validate AuthHeader (token: %s) from %s; dropping message",
  186. local_addr, auth_token.getClass().getSimpleName(), msg.src());
  187. sendRejectionMessage(gms_hdr.getType(), msg.getSrc(), "authentication failed");
  188. return false;
  189. }
  190. }
  191. return true;
  192. }
  193. protected void sendRejectionMessage(byte type, Address dest, String error_msg) {
  194. switch(type) {
  195. case GMS.GmsHeader.JOIN_REQ:
  196. case GMS.GmsHeader.JOIN_REQ_WITH_STATE_TRANSFER:
  197. sendJoinRejectionMessage(dest, error_msg);
  198. break;
  199. case GMS.GmsHeader.MERGE_REQ:
  200. sendMergeRejectionMessage(dest);
  201. break;
  202. }
  203. }
  204. protected void sendJoinRejectionMessage(Address dest, String error_msg) {
  205. if(dest == null)
  206. return;
  207. JoinRsp joinRes=new JoinRsp(error_msg); // specify the error message on the JoinRsp
  208. Message msg = new Message(dest).putHeader(GMS_ID, new GMS.GmsHeader(GMS.GmsHeader.JOIN_RSP))
  209. .setBuffer(GMS.marshal(joinRes));
  210. if(this.authenticate_coord)
  211. msg.putHeader(this.id, new AuthHeader(this.auth_token));
  212. down_prot.down(msg);
  213. }
  214. protected void sendMergeRejectionMessage(Address dest) {
  215. GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.MERGE_RSP).setMergeRejected(true);
  216. Message msg=new Message(dest).setFlag(Message.Flag.OOB).putHeader(GMS_ID, hdr);
  217. if(this.authenticate_coord)
  218. msg.putHeader(this.id, new AuthHeader(this.auth_token));
  219. log.debug("merge response=%s", hdr);
  220. down_prot.down(msg);
  221. }
  222. protected boolean callUpHandlers(Message msg) {
  223. boolean pass_up=true;
  224. for(UpHandler handler: up_handlers) {
  225. if(!handler.handleUpMessage(msg))
  226. pass_up=false;
  227. }
  228. return pass_up;
  229. }
  230. protected static GMS.GmsHeader getGMSHeader(Message msg) {
  231. Header hdr = msg.getHeader(GMS_ID);
  232. if(hdr instanceof GMS.GmsHeader)
  233. return (GMS.GmsHeader)hdr;
  234. return null;
  235. }
  236. }