001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.security.provider.example; 019 020import java.nio.charset.StandardCharsets; 021import java.security.Provider; 022import java.util.Map; 023import javax.security.auth.callback.Callback; 024import javax.security.auth.callback.CallbackHandler; 025import javax.security.auth.callback.NameCallback; 026import javax.security.auth.callback.PasswordCallback; 027import javax.security.sasl.AuthorizeCallback; 028import javax.security.sasl.Sasl; 029import javax.security.sasl.SaslException; 030import javax.security.sasl.SaslServer; 031import javax.security.sasl.SaslServerFactory; 032import org.apache.yetus.audience.InterfaceAudience; 033 034/** 035 * This class was copied from Hadoop Common (3.1.2) and subsequently modified. 036 */ 037@InterfaceAudience.Private 038public class SaslPlainServer implements SaslServer { 039 @SuppressWarnings("serial") 040 public static class SecurityProvider extends Provider { 041 public SecurityProvider() { 042 super("SaslPlainServer", 1.0, "SASL PLAIN Authentication Server"); 043 put("SaslServerFactory.PLAIN", SaslPlainServerFactory.class.getName()); 044 } 045 } 046 047 public static class SaslPlainServerFactory implements SaslServerFactory { 048 @Override 049 public SaslServer createSaslServer(String mechanism, String protocol, String serverName, 050 Map<String, ?> props, CallbackHandler cbh) throws SaslException { 051 return "PLAIN".equals(mechanism) ? new SaslPlainServer(cbh) : null; 052 } 053 054 @Override 055 public String[] getMechanismNames(Map<String, ?> props) { 056 return (props == null) || "false".equals(props.get(Sasl.POLICY_NOPLAINTEXT)) 057 ? new String[] { "PLAIN" } 058 : new String[0]; 059 } 060 } 061 062 private CallbackHandler cbh; 063 private boolean completed; 064 private String authz; 065 066 SaslPlainServer(CallbackHandler callback) { 067 this.cbh = callback; 068 } 069 070 @Override 071 public String getMechanismName() { 072 return "PLAIN"; 073 } 074 075 @Override 076 public byte[] evaluateResponse(byte[] response) throws SaslException { 077 if (completed) { 078 throw new IllegalStateException("PLAIN authentication has completed"); 079 } 080 if (response == null) { 081 throw new IllegalArgumentException("Received null response"); 082 } 083 try { 084 String payload; 085 try { 086 payload = new String(response, StandardCharsets.UTF_8); 087 } catch (Exception e) { 088 throw new IllegalArgumentException("Received corrupt response", e); 089 } 090 // [ authz, authn, password ] 091 String[] parts = payload.split("\u0000", 3); 092 if (parts.length != 3) { 093 throw new IllegalArgumentException("Received corrupt response"); 094 } 095 if (parts[0].isEmpty()) { // authz = authn 096 parts[0] = parts[1]; 097 } 098 099 NameCallback nc = new NameCallback("SASL PLAIN"); 100 nc.setName(parts[1]); 101 PasswordCallback pc = new PasswordCallback("SASL PLAIN", false); 102 pc.setPassword(parts[2].toCharArray()); 103 AuthorizeCallback ac = new AuthorizeCallback(parts[1], parts[0]); 104 cbh.handle(new Callback[] { nc, pc, ac }); 105 if (ac.isAuthorized()) { 106 authz = ac.getAuthorizedID(); 107 } 108 } catch (Exception e) { 109 throw new SaslException("PLAIN auth failed: " + e.toString(), e); 110 } finally { 111 completed = true; 112 } 113 return null; 114 } 115 116 private void throwIfNotComplete() { 117 if (!completed) { 118 throw new IllegalStateException("PLAIN authentication not completed"); 119 } 120 } 121 122 @Override 123 public boolean isComplete() { 124 return completed; 125 } 126 127 @Override 128 public String getAuthorizationID() { 129 throwIfNotComplete(); 130 return authz; 131 } 132 133 @Override 134 public Object getNegotiatedProperty(String propName) { 135 throwIfNotComplete(); 136 return Sasl.QOP.equals(propName) ? "auth" : null; 137 } 138 139 @Override 140 public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException { 141 throwIfNotComplete(); 142 throw new IllegalStateException("PLAIN supports neither integrity nor privacy"); 143 } 144 145 @Override 146 public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException { 147 throwIfNotComplete(); 148 throw new IllegalStateException("PLAIN supports neither integrity nor privacy"); 149 } 150 151 @Override 152 public void dispose() throws SaslException { 153 cbh = null; 154 authz = null; 155 } 156}