Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/sun/security/krb5/auto/Context.java
38853 views
/*1* Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import com.sun.security.auth.module.Krb5LoginModule;24import java.io.IOException;25import java.lang.reflect.InvocationTargetException;26import java.security.PrivilegedActionException;27import java.security.PrivilegedExceptionAction;28import java.security.Key;29import java.util.Arrays;30import java.util.HashMap;31import java.util.Map;32import java.util.Set;33import javax.security.auth.Subject;34import javax.security.auth.callback.Callback;35import javax.security.auth.callback.CallbackHandler;36import javax.security.auth.callback.NameCallback;37import javax.security.auth.callback.PasswordCallback;38import javax.security.auth.callback.UnsupportedCallbackException;39import javax.security.auth.kerberos.KerberosKey;40import javax.security.auth.kerberos.KerberosTicket;41import javax.security.auth.login.LoginContext;42import org.ietf.jgss.GSSContext;43import org.ietf.jgss.GSSCredential;44import org.ietf.jgss.GSSException;45import org.ietf.jgss.GSSManager;46import org.ietf.jgss.GSSName;47import org.ietf.jgss.MessageProp;48import org.ietf.jgss.Oid;49import sun.security.jgss.krb5.Krb5Util;50import sun.security.krb5.Credentials;51import sun.security.krb5.internal.ccache.CredentialsCache;5253import com.sun.security.jgss.ExtendedGSSContext;54import com.sun.security.jgss.InquireType;55import com.sun.security.jgss.AuthorizationDataEntry;56import com.sun.security.jgss.ExtendedGSSCredential;57import java.io.ByteArrayInputStream;58import java.io.ByteArrayOutputStream;59import java.security.Principal;6061/**62* Context of a JGSS subject, encapsulating Subject and GSSContext.63*64* Three "constructors", which acquire the (private) credentials and fill65* it into the Subject:66*67* 1. static fromJAAS(): Creates a Context using a JAAS login config entry68* 2. static fromUserPass(): Creates a Context using a username and a password69* 3. delegated(): A new context which uses the delegated credentials from a70* previously established acceptor Context71*72* Two context initiators, which create the GSSContext object inside:73*74* 1. startAsClient()75* 2. startAsServer()76*77* Privileged action:78* doAs(): Performs an action in the name of the Subject79*80* Handshake process:81* static handShake(initiator, acceptor)82*83* A four-phase typical data communication which includes all four GSS84* actions (wrap, unwrap, getMic and veryfyMiC):85* static transmit(message, from, to)86*/87public class Context {8889private Subject s;90private ExtendedGSSContext x;91private String name;92private GSSCredential cred; // see static method delegated().9394static boolean usingStream = false;9596private Context() {}9798/**99* Using the delegated credentials from a previous acceptor100* @param c101*/102public Context delegated() throws Exception {103Context out = new Context();104out.s = s;105try {106out.cred = Subject.doAs(s, new PrivilegedExceptionAction<GSSCredential>() {107@Override108public GSSCredential run() throws Exception {109GSSCredential cred = x.getDelegCred();110if (cred == null && x.getCredDelegState() ||111cred != null && !x.getCredDelegState()) {112throw new Exception("getCredDelegState not match");113}114return cred;115}116});117} catch (PrivilegedActionException pae) {118throw pae.getException();119}120out.name = name + " as " + out.cred.getName().toString();121return out;122}123124/**125* No JAAS login at all, can be used to test JGSS without JAAS126*/127public static Context fromThinAir() throws Exception {128Context out = new Context();129out.s = new Subject();130return out;131}132133/**134* Logins with a JAAS login config entry name135*/136public static Context fromJAAS(final String name) throws Exception {137Context out = new Context();138out.name = name;139LoginContext lc = new LoginContext(name);140lc.login();141out.s = lc.getSubject();142return out;143}144145/**146* Logins with username/password as a new Subject147*/148public static Context fromUserPass(149String user, char[] pass, boolean storeKey) throws Exception {150return fromUserPass(new Subject(), user, pass, storeKey);151}152153/**154* Logins with username/password as an existing Subject. The155* same subject can be used multiple times to simulate multiple logins.156* @param s existing subject157*/158public static Context fromUserPass(Subject s,159String user, char[] pass, boolean storeKey) throws Exception {160Context out = new Context();161out.name = user;162out.s = s;163Krb5LoginModule krb5 = new Krb5LoginModule();164Map<String, String> map = new HashMap<>();165Map<String, Object> shared = new HashMap<>();166167if (storeKey) {168map.put("storeKey", "true");169}170171if (pass != null) {172krb5.initialize(out.s, new CallbackHandler() {173@Override174public void handle(Callback[] callbacks)175throws IOException, UnsupportedCallbackException {176for (Callback cb: callbacks) {177if (cb instanceof NameCallback) {178((NameCallback)cb).setName(user);179} else if (cb instanceof PasswordCallback) {180((PasswordCallback)cb).setPassword(pass);181}182}183}184}, shared, map);185} else {186map.put("doNotPrompt", "true");187map.put("useTicketCache", "true");188if (user != null) {189map.put("principal", user);190}191krb5.initialize(out.s, null, shared, map);192}193194krb5.login();195krb5.commit();196197return out;198}199200/**201* Logins with username/keytab as an existing Subject. The202* same subject can be used multiple times to simulate multiple logins.203* @param s existing subject204*/205public static Context fromUserKtab(206String user, String ktab, boolean storeKey) throws Exception {207return fromUserKtab(new Subject(), user, ktab, storeKey);208}209210/**211* Logins with username/keytab as a new subject,212*/213public static Context fromUserKtab(Subject s,214String user, String ktab, boolean storeKey) throws Exception {215Context out = new Context();216out.name = user;217out.s = s;218Krb5LoginModule krb5 = new Krb5LoginModule();219Map<String, String> map = new HashMap<>();220221map.put("isInitiator", "false");222map.put("doNotPrompt", "true");223map.put("useTicketCache", "false");224map.put("useKeyTab", "true");225map.put("keyTab", ktab);226map.put("principal", user);227if (storeKey) {228map.put("storeKey", "true");229}230231krb5.initialize(out.s, null, null, map);232krb5.login();233krb5.commit();234return out;235}236237/**238* Starts as a client239* @param target communication peer240* @param mech GSS mech241* @throws java.lang.Exception242*/243public void startAsClient(final String target, final Oid mech) throws Exception {244doAs(new Action() {245@Override246public byte[] run(Context me, byte[] dummy) throws Exception {247GSSManager m = GSSManager.getInstance();248me.x = (ExtendedGSSContext)m.createContext(249target.indexOf('@') < 0 ?250m.createName(target, null) :251m.createName(target, GSSName.NT_HOSTBASED_SERVICE),252mech,253cred,254GSSContext.DEFAULT_LIFETIME);255return null;256}257}, null);258}259260/**261* Starts as a server262* @param mech GSS mech263* @throws java.lang.Exception264*/265public void startAsServer(final Oid mech) throws Exception {266startAsServer(null, mech, false);267}268269public void startAsServer(final String name, final Oid mech) throws Exception {270startAsServer(name, mech, false);271}272/**273* Starts as a server with the specified service name274* @param name the service name275* @param mech GSS mech276* @throws java.lang.Exception277*/278public void startAsServer(final String name, final Oid mech, final boolean asInitiator) throws Exception {279doAs(new Action() {280@Override281public byte[] run(Context me, byte[] dummy) throws Exception {282GSSManager m = GSSManager.getInstance();283me.cred = m.createCredential(284name == null ? null :285(name.indexOf('@') < 0 ?286m.createName(name, null) :287m.createName(name, GSSName.NT_HOSTBASED_SERVICE)),288GSSCredential.INDEFINITE_LIFETIME,289mech,290asInitiator?291GSSCredential.INITIATE_AND_ACCEPT:292GSSCredential.ACCEPT_ONLY);293me.x = (ExtendedGSSContext)m.createContext(me.cred);294return null;295}296}, null);297}298299/**300* Accesses the internal GSSContext object. Currently it's used for --301*302* 1. calling requestXXX() before handshake303* 2. accessing source name304*305* Note: If the application needs to do any privileged call on this306* object, please use doAs(). Otherwise, it can be done directly. The307* methods listed above are all non-privileged calls.308*309* @return the GSSContext object310*/311public ExtendedGSSContext x() {312return x;313}314315/**316* Accesses the internal subject.317* @return the subject318*/319public Subject s() {320return s;321}322323/**324* Returns the cred inside, if there is one325*/326public GSSCredential cred() {327return cred;328}329330/**331* Disposes the GSSContext within332* @throws org.ietf.jgss.GSSException333*/334public void dispose() throws GSSException {335x.dispose();336}337338/**339* Does something using the Subject inside340* @param action the action341* @param in the input byte342* @return the output byte343* @throws java.lang.Exception344*/345public byte[] doAs(final Action action, final byte[] in) throws Exception {346try {347return Subject.doAs(s, new PrivilegedExceptionAction<byte[]>() {348349@Override350public byte[] run() throws Exception {351return action.run(Context.this, in);352}353});354} catch (PrivilegedActionException pae) {355throw pae.getException();356}357}358359/**360* Prints status of GSSContext and Subject361* @throws java.lang.Exception362*/363public void status() throws Exception {364System.out.println("STATUS OF " + name.toUpperCase());365try {366StringBuffer sb = new StringBuffer();367if (x.getAnonymityState()) {368sb.append("anon, ");369}370if (x.getConfState()) {371sb.append("conf, ");372}373if (x.getCredDelegState()) {374sb.append("deleg, ");375}376if (x.getIntegState()) {377sb.append("integ, ");378}379if (x.getMutualAuthState()) {380sb.append("mutual, ");381}382if (x.getReplayDetState()) {383sb.append("rep det, ");384}385if (x.getSequenceDetState()) {386sb.append("seq det, ");387}388if (x instanceof ExtendedGSSContext) {389if (((ExtendedGSSContext)x).getDelegPolicyState()) {390sb.append("deleg policy, ");391}392}393System.out.println("Context status of " + name + ": " + sb.toString());394System.out.println(x.getSrcName() + " -> " + x.getTargName());395} catch (Exception e) {396;// Don't care397}398if (s != null) {399System.out.println("====== START SUBJECT CONTENT =====");400for (Principal p: s.getPrincipals()) {401System.out.println(" Principal: " + p);402}403for (Object o : s.getPublicCredentials()) {404System.out.println(" " + o.getClass());405System.out.println(" " + o);406}407System.out.println("====== Private Credentials Set ======");408for (Object o : s.getPrivateCredentials()) {409System.out.println(" " + o.getClass());410if (o instanceof KerberosTicket) {411KerberosTicket kt = (KerberosTicket) o;412System.out.println(" " + kt.getServer() + " for " + kt.getClient());413} else if (o instanceof KerberosKey) {414KerberosKey kk = (KerberosKey) o;415System.out.print(" " + kk.getKeyType() + " " + kk.getVersionNumber() + " " + kk.getAlgorithm() + " ");416for (byte b : kk.getEncoded()) {417System.out.printf("%02X", b & 0xff);418}419System.out.println();420} else if (o instanceof Map) {421Map map = (Map) o;422for (Object k : map.keySet()) {423System.out.println(" " + k + ": " + map.get(k));424}425} else {426System.out.println(" " + o);427}428}429System.out.println("====== END SUBJECT CONTENT =====");430}431if (x != null && x instanceof ExtendedGSSContext) {432if (x.isEstablished()) {433ExtendedGSSContext ex = (ExtendedGSSContext)x;434Key k = (Key)ex.inquireSecContext(435InquireType.KRB5_GET_SESSION_KEY);436if (k == null) {437throw new Exception("Session key cannot be null");438}439System.out.println("Session key is: " + k);440boolean[] flags = (boolean[])ex.inquireSecContext(441InquireType.KRB5_GET_TKT_FLAGS);442if (flags == null) {443throw new Exception("Ticket flags cannot be null");444}445System.out.println("Ticket flags is: " + Arrays.toString(flags));446String authTime = (String)ex.inquireSecContext(447InquireType.KRB5_GET_AUTHTIME);448if (authTime == null) {449throw new Exception("Auth time cannot be null");450}451System.out.println("AuthTime is: " + authTime);452if (!x.isInitiator()) {453AuthorizationDataEntry[] ad = (AuthorizationDataEntry[])ex.inquireSecContext(454InquireType.KRB5_GET_AUTHZ_DATA);455System.out.println("AuthzData is: " + Arrays.toString(ad));456}457}458}459}460461public byte[] wrap(byte[] t, final boolean privacy)462throws Exception {463return doAs(new Action() {464@Override465public byte[] run(Context me, byte[] input) throws Exception {466System.out.printf("wrap %s privacy from %s: ", privacy?"with":"without", me.name);467MessageProp p1 = new MessageProp(0, privacy);468byte[] out;469if (usingStream) {470ByteArrayOutputStream os = new ByteArrayOutputStream();471me.x.wrap(new ByteArrayInputStream(input), os, p1);472out = os.toByteArray();473} else {474out = me.x.wrap(input, 0, input.length, p1);475}476System.out.println(printProp(p1));477if ((x.getConfState() && privacy) != p1.getPrivacy()) {478throw new Exception("unexpected privacy status");479}480return out;481}482}, t);483}484485public byte[] unwrap(byte[] t, final boolean privacyExpected)486throws Exception {487return doAs(new Action() {488@Override489public byte[] run(Context me, byte[] input) throws Exception {490System.out.printf("unwrap from %s", me.name);491MessageProp p1 = new MessageProp(0, true);492byte[] bytes;493if (usingStream) {494ByteArrayOutputStream os = new ByteArrayOutputStream();495me.x.unwrap(new ByteArrayInputStream(input), os, p1);496bytes = os.toByteArray();497} else {498bytes = me.x.unwrap(input, 0, input.length, p1);499}500System.out.println(printProp(p1));501if (p1.getPrivacy() != privacyExpected) {502throw new Exception("Unexpected privacy: " + p1.getPrivacy());503}504return bytes;505}506}, t);507}508509public byte[] getMic(byte[] t) throws Exception {510return doAs(new Action() {511@Override512public byte[] run(Context me, byte[] input) throws Exception {513MessageProp p1 = new MessageProp(0, true);514byte[] bytes;515p1 = new MessageProp(0, true);516System.out.printf("getMic from %s: ", me.name);517if (usingStream) {518ByteArrayOutputStream os = new ByteArrayOutputStream();519me.x.getMIC(new ByteArrayInputStream(input), os, p1);520bytes = os.toByteArray();521} else {522bytes = me.x.getMIC(input, 0, input.length, p1);523}524System.out.println(printProp(p1));525return bytes;526}527}, t);528}529530public void verifyMic(byte[] t, final byte[] msg) throws Exception {531doAs(new Action() {532@Override533public byte[] run(Context me, byte[] input) throws Exception {534MessageProp p1 = new MessageProp(0, true);535System.out.printf("verifyMic from %s: ", me.name);536if (usingStream) {537me.x.verifyMIC(new ByteArrayInputStream(input),538new ByteArrayInputStream(msg), p1);539} else {540me.x.verifyMIC(input, 0, input.length,541msg, 0, msg.length,542p1);543}544System.out.println(printProp(p1));545if (p1.isUnseqToken() || p1.isOldToken()546|| p1.isDuplicateToken() || p1.isGapToken()) {547throw new Exception("Wrong sequence number detected");548}549return null;550}551}, t);552}553554/**555* Transmits a message from one Context to another. The sender wraps the556* message and sends it to the receiver. The receiver unwraps it, creates557* a MIC of the clear text and sends it back to the sender. The sender558* verifies the MIC against the message sent earlier.559* @param message the message560* @param s1 the sender561* @param s2 the receiver562* @throws java.lang.Exception If anything goes wrong563*/564static public void transmit(String message, final Context s1,565final Context s2) throws Exception {566transmit(message.getBytes(), s1, s2);567}568569/**570* Transmits a message from one Context to another. The sender wraps the571* message and sends it to the receiver. The receiver unwraps it, creates572* a MIC of the clear text and sends it back to the sender. The sender573* verifies the MIC against the message sent earlier.574* @param messageBytes the message575* @param s1 the sender576* @param s2 the receiver577* @throws java.lang.Exception If anything goes wrong578*/579static public void transmit(byte[] messageBytes, final Context s1,580final Context s2) throws Exception {581System.out.printf("-------------------- TRANSMIT from %s to %s------------------------\n",582s1.name, s2.name);583byte[] wrapped = s1.wrap(messageBytes, true);584byte[] unwrapped = s2.unwrap(wrapped, s2.x.getConfState());585if (!Arrays.equals(messageBytes, unwrapped)) {586throw new Exception("wrap/unwrap mismatch");587}588byte[] mic = s2.getMic(unwrapped);589s1.verifyMic(mic, messageBytes);590}591592/**593* Returns a string description of a MessageProp object594* @param prop the object595* @return the description596*/597static public String printProp(MessageProp prop) {598StringBuffer sb = new StringBuffer();599sb.append("MessagePop: ");600sb.append("QOP="+ prop.getQOP() + ", ");601sb.append(prop.getPrivacy()?"privacy, ":"");602sb.append(prop.isDuplicateToken()?"dup, ":"");603sb.append(prop.isGapToken()?"gap, ":"");604sb.append(prop.isOldToken()?"old, ":"");605sb.append(prop.isUnseqToken()?"unseq, ":"");606if (prop.getMinorStatus() != 0) {607sb.append(prop.getMinorString()+ "(" + prop.getMinorStatus()+")");608}609return sb.toString();610}611612public Context impersonate(final String someone) throws Exception {613try {614GSSCredential creds = Subject.doAs(s, new PrivilegedExceptionAction<GSSCredential>() {615@Override616public GSSCredential run() throws Exception {617GSSManager m = GSSManager.getInstance();618GSSName other = m.createName(someone, GSSName.NT_USER_NAME);619if (Context.this.cred == null) {620Context.this.cred = m.createCredential(GSSCredential.INITIATE_ONLY);621}622return ((ExtendedGSSCredential)Context.this.cred).impersonate(other);623}624});625Context out = new Context();626out.s = s;627out.cred = creds;628out.name = name + " as " + out.cred.getName().toString();629return out;630} catch (PrivilegedActionException pae) {631Exception e = pae.getException();632if (e instanceof InvocationTargetException) {633throw (Exception)((InvocationTargetException) e).getTargetException();634} else {635throw e;636}637}638}639640public byte[] take(final byte[] in) throws Exception {641return doAs(new Action() {642@Override643public byte[] run(Context me, byte[] input) throws Exception {644if (me.x.isEstablished()) {645System.out.println(name + " side established");646if (input != null) {647throw new Exception("Context established but " +648"still receive token at " + name);649}650return null;651} else {652if (me.x.isInitiator()) {653System.out.println(name + " call initSecContext");654return me.x.initSecContext(input, 0, input.length);655} else {656System.out.println(name + " call acceptSecContext");657return me.x.acceptSecContext(input, 0, input.length);658}659}660}661}, in);662}663664/**665* Saves the tickets to a ccache file.666*667* @param file pathname of the ccache file668* @return true if created, false otherwise.669*/670public boolean ccache(String file) throws Exception {671Set<KerberosTicket> tickets672= s.getPrivateCredentials(KerberosTicket.class);673if (tickets != null && !tickets.isEmpty()) {674CredentialsCache cc = null;675for (KerberosTicket t : tickets) {676Credentials cred = Krb5Util.ticketToCreds(t);677if (cc == null) {678cc = CredentialsCache.create(cred.getClient(), file);679}680cc.update(cred.toCCacheCreds());681}682if (cc != null) {683cc.save();684return true;685}686}687return false;688}689690/**691* Handshake (security context establishment process) between two Contexts692* @param c the initiator693* @param s the acceptor694* @throws java.lang.Exception695*/696static public void handshake(final Context c, final Context s) throws Exception {697byte[] t = new byte[0];698while (true) {699if (t != null || !c.x.isEstablished()) t = c.take(t);700if (t != null || !s.x.isEstablished()) t = s.take(t);701if (c.x.isEstablished() && s.x.isEstablished()) break;702}703}704}705706707