Context.java 25.4 KB
Newer Older
W
weijun 已提交
1
/*
2
 * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
W
weijun 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
19 20 21
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
W
weijun 已提交
22 23 24
 */

import com.sun.security.auth.module.Krb5LoginModule;
25
import java.io.IOException;
26
import java.lang.reflect.InvocationTargetException;
W
weijun 已提交
27 28
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
29
import java.security.Key;
W
weijun 已提交
30 31 32
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
33
import java.util.Set;
W
weijun 已提交
34
import javax.security.auth.Subject;
35 36 37 38 39
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
W
weijun 已提交
40 41 42 43 44 45 46 47 48 49
import javax.security.auth.kerberos.KerberosKey;
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.login.LoginContext;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.MessageProp;
import org.ietf.jgss.Oid;
50 51 52 53
import sun.security.jgss.krb5.Krb5Util;
import sun.security.krb5.Credentials;
import sun.security.krb5.internal.ccache.CredentialsCache;

54 55
import com.sun.security.jgss.ExtendedGSSContext;
import com.sun.security.jgss.InquireType;
56
import com.sun.security.jgss.AuthorizationDataEntry;
57
import com.sun.security.jgss.ExtendedGSSCredential;
58 59
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
60
import java.security.Principal;
W
weijun 已提交
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90

/**
 * Context of a JGSS subject, encapsulating Subject and GSSContext.
 *
 * Three "constructors", which acquire the (private) credentials and fill
 * it into the Subject:
 *
 * 1. static fromJAAS(): Creates a Context using a JAAS login config entry
 * 2. static fromUserPass(): Creates a Context using a username and a password
 * 3. delegated(): A new context which uses the delegated credentials from a
 *    previously established acceptor Context
 *
 * Two context initiators, which create the GSSContext object inside:
 *
 * 1. startAsClient()
 * 2. startAsServer()
 *
 * Privileged action:
 *    doAs(): Performs an action in the name of the Subject
 *
 * Handshake process:
 *    static handShake(initiator, acceptor)
 *
 * A four-phase typical data communication which includes all four GSS
 * actions (wrap, unwrap, getMic and veryfyMiC):
 *    static transmit(message, from, to)
 */
public class Context {

    private Subject s;
W
weijun 已提交
91
    private ExtendedGSSContext x;
W
weijun 已提交
92 93 94
    private String name;
    private GSSCredential cred;     // see static method delegated().

95 96
    static boolean usingStream = false;

W
weijun 已提交
97 98 99 100 101 102 103 104 105
    private Context() {}

    /**
     * Using the delegated credentials from a previous acceptor
     * @param c
     */
    public Context delegated() throws Exception {
        Context out = new Context();
        out.s = s;
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
        try {
            out.cred = Subject.doAs(s, new PrivilegedExceptionAction<GSSCredential>() {
                @Override
                public GSSCredential run() throws Exception {
                    GSSCredential cred = x.getDelegCred();
                    if (cred == null && x.getCredDelegState() ||
                            cred != null && !x.getCredDelegState()) {
                        throw new Exception("getCredDelegState not match");
                    }
                    return cred;
                }
            });
        } catch (PrivilegedActionException pae) {
            throw pae.getException();
        }
W
weijun 已提交
121 122 123 124
        out.name = name + " as " + out.cred.getName().toString();
        return out;
    }

W
weijun 已提交
125 126 127 128 129 130 131 132 133
    /**
     * No JAAS login at all, can be used to test JGSS without JAAS
     */
    public static Context fromThinAir() throws Exception {
        Context out = new Context();
        out.s = new Subject();
        return out;
    }

W
weijun 已提交
134 135 136 137 138 139 140 141 142 143 144 145
    /**
     * Logins with a JAAS login config entry name
     */
    public static Context fromJAAS(final String name) throws Exception {
        Context out = new Context();
        out.name = name;
        LoginContext lc = new LoginContext(name);
        lc.login();
        out.s = lc.getSubject();
        return out;
    }

146 147 148
    /**
     * Logins with username/password as a new Subject
     */
W
weijun 已提交
149 150
    public static Context fromUserPass(
            String user, char[] pass, boolean storeKey) throws Exception {
151
        return fromUserPass(new Subject(), user, pass, storeKey);
W
weijun 已提交
152
    }
W
weijun 已提交
153

W
weijun 已提交
154
    /**
155 156 157
     * Logins with username/password as an existing Subject. The
     * same subject can be used multiple times to simulate multiple logins.
     * @param s existing subject
W
weijun 已提交
158
     */
W
weijun 已提交
159 160
    public static Context fromUserPass(Subject s,
            String user, char[] pass, boolean storeKey) throws Exception {
W
weijun 已提交
161 162
        Context out = new Context();
        out.name = user;
163
        out.s = s;
W
weijun 已提交
164
        Krb5LoginModule krb5 = new Krb5LoginModule();
165 166
        Map<String, String> map = new HashMap<>();
        Map<String, Object> shared = new HashMap<>();
167

168 169 170 171
        if (storeKey) {
            map.put("storeKey", "true");
        }

172
        if (pass != null) {
173 174 175 176 177 178 179 180 181 182 183 184 185
            krb5.initialize(out.s, new CallbackHandler() {
                @Override
                public void handle(Callback[] callbacks)
                        throws IOException, UnsupportedCallbackException {
                    for (Callback cb: callbacks) {
                        if (cb instanceof NameCallback) {
                            ((NameCallback)cb).setName(user);
                        } else if (cb instanceof PasswordCallback) {
                            ((PasswordCallback)cb).setPassword(pass);
                        }
                    }
                }
            }, shared, map);
186 187 188 189 190 191
        } else {
            map.put("doNotPrompt", "true");
            map.put("useTicketCache", "true");
            if (user != null) {
                map.put("principal", user);
            }
192
            krb5.initialize(out.s, null, shared, map);
W
weijun 已提交
193 194 195 196
        }

        krb5.login();
        krb5.commit();
197

W
weijun 已提交
198 199 200
        return out;
    }

201
    /**
202 203 204
     * Logins with username/keytab as an existing Subject. The
     * same subject can be used multiple times to simulate multiple logins.
     * @param s existing subject
205
     */
206 207 208 209 210 211 212 213 214 215
    public static Context fromUserKtab(
            String user, String ktab, boolean storeKey) throws Exception {
        return fromUserKtab(new Subject(), user, ktab, storeKey);
    }

    /**
     * Logins with username/keytab as a new subject,
     */
    public static Context fromUserKtab(Subject s,
            String user, String ktab, boolean storeKey) throws Exception {
216 217
        Context out = new Context();
        out.name = user;
218
        out.s = s;
219
        Krb5LoginModule krb5 = new Krb5LoginModule();
220
        Map<String, String> map = new HashMap<>();
221

W
weijun 已提交
222
        map.put("isInitiator", "false");
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
        map.put("doNotPrompt", "true");
        map.put("useTicketCache", "false");
        map.put("useKeyTab", "true");
        map.put("keyTab", ktab);
        map.put("principal", user);
        if (storeKey) {
            map.put("storeKey", "true");
        }

        krb5.initialize(out.s, null, null, map);
        krb5.login();
        krb5.commit();
        return out;
    }

W
weijun 已提交
238 239 240 241 242 243 244 245 246 247 248
    /**
     * Starts as a client
     * @param target communication peer
     * @param mech GSS mech
     * @throws java.lang.Exception
     */
    public void startAsClient(final String target, final Oid mech) throws Exception {
        doAs(new Action() {
            @Override
            public byte[] run(Context me, byte[] dummy) throws Exception {
                GSSManager m = GSSManager.getInstance();
W
weijun 已提交
249 250
                me.x = (ExtendedGSSContext)m.createContext(
                          target.indexOf('@') < 0 ?
W
weijun 已提交
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
                            m.createName(target, null) :
                            m.createName(target, GSSName.NT_HOSTBASED_SERVICE),
                        mech,
                        cred,
                        GSSContext.DEFAULT_LIFETIME);
                return null;
            }
        }, null);
    }

    /**
     * Starts as a server
     * @param mech GSS mech
     * @throws java.lang.Exception
     */
    public void startAsServer(final Oid mech) throws Exception {
267
        startAsServer(null, mech, false);
W
weijun 已提交
268 269
    }

270 271 272
    public void startAsServer(final String name, final Oid mech) throws Exception {
        startAsServer(name, mech, false);
    }
W
weijun 已提交
273 274 275 276 277 278
    /**
     * Starts as a server with the specified service name
     * @param name the service name
     * @param mech GSS mech
     * @throws java.lang.Exception
     */
279
    public void startAsServer(final String name, final Oid mech, final boolean asInitiator) throws Exception {
W
weijun 已提交
280 281 282 283
        doAs(new Action() {
            @Override
            public byte[] run(Context me, byte[] dummy) throws Exception {
                GSSManager m = GSSManager.getInstance();
284
                me.cred = m.createCredential(
W
weijun 已提交
285 286 287 288
                        name == null ? null :
                          (name.indexOf('@') < 0 ?
                            m.createName(name, null) :
                            m.createName(name, GSSName.NT_HOSTBASED_SERVICE)),
W
weijun 已提交
289 290
                        GSSCredential.INDEFINITE_LIFETIME,
                        mech,
291 292 293 294
                        asInitiator?
                                GSSCredential.INITIATE_AND_ACCEPT:
                                GSSCredential.ACCEPT_ONLY);
                me.x = (ExtendedGSSContext)m.createContext(me.cred);
W
weijun 已提交
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
                return null;
            }
        }, null);
    }

    /**
     * Accesses the internal GSSContext object. Currently it's used for --
     *
     * 1. calling requestXXX() before handshake
     * 2. accessing source name
     *
     * Note: If the application needs to do any privileged call on this
     * object, please use doAs(). Otherwise, it can be done directly. The
     * methods listed above are all non-privileged calls.
     *
     * @return the GSSContext object
     */
W
weijun 已提交
312
    public ExtendedGSSContext x() {
W
weijun 已提交
313 314 315
        return x;
    }

W
weijun 已提交
316 317 318 319 320 321 322 323
    /**
     * Accesses the internal subject.
     * @return the subject
     */
    public Subject s() {
        return s;
    }

324 325 326 327 328 329 330
    /**
     * Returns the cred inside, if there is one
     */
    public GSSCredential cred() {
        return cred;
    }

W
weijun 已提交
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
    /**
     * Disposes the GSSContext within
     * @throws org.ietf.jgss.GSSException
     */
    public void dispose() throws GSSException {
        x.dispose();
    }

    /**
     * Does something using the Subject inside
     * @param action the action
     * @param in the input byte
     * @return the output byte
     * @throws java.lang.Exception
     */
    public byte[] doAs(final Action action, final byte[] in) throws Exception {
        try {
            return Subject.doAs(s, new PrivilegedExceptionAction<byte[]>() {

                @Override
                public byte[] run() throws Exception {
                    return action.run(Context.this, in);
                }
            });
        } catch (PrivilegedActionException pae) {
            throw pae.getException();
        }
    }

    /**
     * Prints status of GSSContext and Subject
     * @throws java.lang.Exception
     */
    public void status() throws Exception {
        System.out.println("STATUS OF " + name.toUpperCase());
        try {
            StringBuffer sb = new StringBuffer();
            if (x.getAnonymityState()) {
                sb.append("anon, ");
            }
            if (x.getConfState()) {
                sb.append("conf, ");
            }
            if (x.getCredDelegState()) {
                sb.append("deleg, ");
            }
            if (x.getIntegState()) {
                sb.append("integ, ");
            }
            if (x.getMutualAuthState()) {
                sb.append("mutual, ");
            }
            if (x.getReplayDetState()) {
                sb.append("rep det, ");
            }
            if (x.getSequenceDetState()) {
                sb.append("seq det, ");
            }
W
weijun 已提交
389 390 391 392 393
            if (x instanceof ExtendedGSSContext) {
                if (((ExtendedGSSContext)x).getDelegPolicyState()) {
                    sb.append("deleg policy, ");
                }
            }
W
weijun 已提交
394 395 396 397 398
            System.out.println("Context status of " + name + ": " + sb.toString());
            System.out.println(x.getSrcName() + " -> " + x.getTargName());
        } catch (Exception e) {
            ;// Don't care
        }
399 400 401 402 403 404 405
        if (s != null) {
            System.out.println("====== START SUBJECT CONTENT =====");
            for (Principal p: s.getPrincipals()) {
                System.out.println("    Principal: " + p);
            }
            for (Object o : s.getPublicCredentials()) {
                System.out.println("    " + o.getClass());
W
weijun 已提交
406
                System.out.println("        " + o);
W
weijun 已提交
407
            }
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
            System.out.println("====== Private Credentials Set ======");
            for (Object o : s.getPrivateCredentials()) {
                System.out.println("    " + o.getClass());
                if (o instanceof KerberosTicket) {
                    KerberosTicket kt = (KerberosTicket) o;
                    System.out.println("        " + kt.getServer() + " for " + kt.getClient());
                } else if (o instanceof KerberosKey) {
                    KerberosKey kk = (KerberosKey) o;
                    System.out.print("        " + kk.getKeyType() + " " + kk.getVersionNumber() + " " + kk.getAlgorithm() + " ");
                    for (byte b : kk.getEncoded()) {
                        System.out.printf("%02X", b & 0xff);
                    }
                    System.out.println();
                } else if (o instanceof Map) {
                    Map map = (Map) o;
                    for (Object k : map.keySet()) {
                        System.out.println("        " + k + ": " + map.get(k));
                    }
                } else {
                    System.out.println("        " + o);
                }
            }
            System.out.println("====== END SUBJECT CONTENT =====");
W
weijun 已提交
431
        }
432 433 434 435 436 437 438 439 440
        if (x != null && x instanceof ExtendedGSSContext) {
            if (x.isEstablished()) {
                ExtendedGSSContext ex = (ExtendedGSSContext)x;
                Key k = (Key)ex.inquireSecContext(
                        InquireType.KRB5_GET_SESSION_KEY);
                if (k == null) {
                    throw new Exception("Session key cannot be null");
                }
                System.out.println("Session key is: " + k);
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
                boolean[] flags = (boolean[])ex.inquireSecContext(
                        InquireType.KRB5_GET_TKT_FLAGS);
                if (flags == null) {
                    throw new Exception("Ticket flags cannot be null");
                }
                System.out.println("Ticket flags is: " + Arrays.toString(flags));
                String authTime = (String)ex.inquireSecContext(
                        InquireType.KRB5_GET_AUTHTIME);
                if (authTime == null) {
                    throw new Exception("Auth time cannot be null");
                }
                System.out.println("AuthTime is: " + authTime);
                if (!x.isInitiator()) {
                    AuthorizationDataEntry[] ad = (AuthorizationDataEntry[])ex.inquireSecContext(
                            InquireType.KRB5_GET_AUTHZ_DATA);
                    System.out.println("AuthzData is: " + Arrays.toString(ad));
                }
458 459
            }
        }
W
weijun 已提交
460 461
    }

462 463 464
    public byte[] wrap(byte[] t, final boolean privacy)
            throws Exception {
        return doAs(new Action() {
W
weijun 已提交
465
            @Override
466 467 468
            public byte[] run(Context me, byte[] input) throws Exception {
                System.out.printf("wrap %s privacy from %s: ", privacy?"with":"without", me.name);
                MessageProp p1 = new MessageProp(0, privacy);
469 470 471
                byte[] out;
                if (usingStream) {
                    ByteArrayOutputStream os = new ByteArrayOutputStream();
472
                    me.x.wrap(new ByteArrayInputStream(input), os, p1);
473 474
                    out = os.toByteArray();
                } else {
475
                    out = me.x.wrap(input, 0, input.length, p1);
476
                }
W
weijun 已提交
477 478 479
                System.out.println(printProp(p1));
                return out;
            }
480 481
        }, t);
    }
W
weijun 已提交
482

483 484 485
    public byte[] unwrap(byte[] t, final boolean privacy)
            throws Exception {
        return doAs(new Action() {
W
weijun 已提交
486 487
            @Override
            public byte[] run(Context me, byte[] input) throws Exception {
488 489
                System.out.printf("unwrap %s privacy from %s: ", privacy?"with":"without", me.name);
                MessageProp p1 = new MessageProp(0, privacy);
490 491 492 493 494 495 496 497
                byte[] bytes;
                if (usingStream) {
                    ByteArrayOutputStream os = new ByteArrayOutputStream();
                    me.x.unwrap(new ByteArrayInputStream(input), os, p1);
                    bytes = os.toByteArray();
                } else {
                    bytes = me.x.unwrap(input, 0, input.length, p1);
                }
W
weijun 已提交
498
                System.out.println(printProp(p1));
499 500 501 502 503 504 505 506 507 508 509
                return bytes;
            }
        }, t);
    }

    public byte[] getMic(byte[] t) throws Exception {
        return doAs(new Action() {
            @Override
            public byte[] run(Context me, byte[] input) throws Exception {
                MessageProp p1 = new MessageProp(0, true);
                byte[] bytes;
W
weijun 已提交
510
                p1 = new MessageProp(0, true);
511
                System.out.printf("getMic from %s: ", me.name);
512 513
                if (usingStream) {
                    ByteArrayOutputStream os = new ByteArrayOutputStream();
514
                    me.x.getMIC(new ByteArrayInputStream(input), os, p1);
515 516
                    bytes = os.toByteArray();
                } else {
517
                    bytes = me.x.getMIC(input, 0, input.length, p1);
518
                }
W
weijun 已提交
519 520 521 522
                System.out.println(printProp(p1));
                return bytes;
            }
        }, t);
523
    }
524

525 526
    public void verifyMic(byte[] t, final byte[] msg) throws Exception {
        doAs(new Action() {
W
weijun 已提交
527 528 529
            @Override
            public byte[] run(Context me, byte[] input) throws Exception {
                MessageProp p1 = new MessageProp(0, true);
530
                System.out.printf("verifyMic from %s: ", me.name);
531 532
                if (usingStream) {
                    me.x.verifyMIC(new ByteArrayInputStream(input),
533
                            new ByteArrayInputStream(msg), p1);
534 535
                } else {
                    me.x.verifyMIC(input, 0, input.length,
536
                            msg, 0, msg.length,
537 538
                            p1);
                }
W
weijun 已提交
539 540 541 542 543 544
                System.out.println(printProp(p1));
                return null;
            }
        }, t);
    }

545 546 547 548 549 550 551 552 553 554
    /**
     * Transmits a message from one Context to another. The sender wraps the
     * message and sends it to the receiver. The receiver unwraps it, creates
     * a MIC of the clear text and sends it back to the sender. The sender
     * verifies the MIC against the message sent earlier.
     * @param message the message
     * @param s1 the sender
     * @param s2 the receiver
     * @throws java.lang.Exception If anything goes wrong
     */
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
    static public void transmit(String message, final Context s1,
                                final Context s2) throws Exception {
        transmit(message.getBytes(), s1, s2);
    }

    /**
     * Transmits a message from one Context to another. The sender wraps the
     * message and sends it to the receiver. The receiver unwraps it, creates
     * a MIC of the clear text and sends it back to the sender. The sender
     * verifies the MIC against the message sent earlier.
     * @param messageBytes the message
     * @param s1 the sender
     * @param s2 the receiver
     * @throws java.lang.Exception If anything goes wrong
     */
    static public void transmit(byte[] messageBytes, final Context s1,
571 572 573 574 575 576 577 578 579 580 581 582
            final Context s2) throws Exception {
        System.out.printf("-------------------- TRANSMIT from %s to %s------------------------\n",
                s1.name, s2.name);
        byte[] wrapped = s1.wrap(messageBytes, true);
        byte[] unwrapped = s2.unwrap(wrapped, true);
        if (!Arrays.equals(messageBytes, unwrapped)) {
            throw new Exception("wrap/unwrap mismatch");
        }
        byte[] mic = s2.getMic(unwrapped);
        s1.verifyMic(mic, messageBytes);
    }

W
weijun 已提交
583 584 585 586 587 588 589 590 591 592 593 594 595 596
    /**
     * Returns a string description of a MessageProp object
     * @param prop the object
     * @return the description
     */
    static public String printProp(MessageProp prop) {
        StringBuffer sb = new StringBuffer();
        sb.append("MessagePop: ");
        sb.append("QOP="+ prop.getQOP() + ", ");
        sb.append(prop.getPrivacy()?"privacy, ":"");
        sb.append(prop.isDuplicateToken()?"dup, ":"");
        sb.append(prop.isGapToken()?"gap, ":"");
        sb.append(prop.isOldToken()?"old, ":"");
        sb.append(prop.isUnseqToken()?"unseq, ":"");
597 598 599
        if (prop.getMinorStatus() != 0) {
            sb.append(prop.getMinorString()+ "(" + prop.getMinorStatus()+")");
        }
W
weijun 已提交
600 601 602
        return sb.toString();
    }

603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
    public Context impersonate(final String someone) throws Exception {
        try {
            GSSCredential creds = Subject.doAs(s, new PrivilegedExceptionAction<GSSCredential>() {
                @Override
                public GSSCredential run() throws Exception {
                    GSSManager m = GSSManager.getInstance();
                    GSSName other = m.createName(someone, GSSName.NT_USER_NAME);
                    if (Context.this.cred == null) {
                        Context.this.cred = m.createCredential(GSSCredential.INITIATE_ONLY);
                    }
                    return ((ExtendedGSSCredential)Context.this.cred).impersonate(other);
                }
            });
            Context out = new Context();
            out.s = s;
            out.cred = creds;
            out.name = name + " as " + out.cred.getName().toString();
            return out;
        } catch (PrivilegedActionException pae) {
622 623 624 625 626 627
            Exception e = pae.getException();
            if (e instanceof InvocationTargetException) {
                throw (Exception)((InvocationTargetException) e).getTargetException();
            } else {
                throw e;
            }
628 629 630
        }
    }

W
weijun 已提交
631 632 633 634 635 636 637 638 639 640 641 642 643
    public byte[] take(final byte[] in) throws Exception {
        return doAs(new Action() {
            @Override
            public byte[] run(Context me, byte[] input) throws Exception {
                if (me.x.isEstablished()) {
                    System.out.println(name + " side established");
                    if (input != null) {
                        throw new Exception("Context established but " +
                                "still receive token at " + name);
                    }
                    return null;
                } else {
                    if (me.x.isInitiator()) {
644
                        System.out.println(name + " call initSecContext");
W
weijun 已提交
645 646
                        return me.x.initSecContext(input, 0, input.length);
                    } else {
647
                        System.out.println(name + " call acceptSecContext");
W
weijun 已提交
648 649 650 651 652 653 654
                        return me.x.acceptSecContext(input, 0, input.length);
                    }
                }
            }
        }, in);
    }

655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
    /**
     * Saves the tickets to a ccache file.
     *
     * @param file pathname of the ccache file
     * @return true if created, false otherwise.
     */
    public boolean ccache(String file) throws Exception {
        Set<KerberosTicket> tickets
                = s.getPrivateCredentials(KerberosTicket.class);
        if (tickets != null && !tickets.isEmpty()) {
            CredentialsCache cc = null;
            for (KerberosTicket t : tickets) {
                Credentials cred = Krb5Util.ticketToCreds(t);
                if (cc == null) {
                    cc = CredentialsCache.create(cred.getClient(), file);
                }
                cc.update(cred.toCCacheCreds());
            }
            if (cc != null) {
                cc.save();
                return true;
            }
        }
        return false;
    }

W
weijun 已提交
681 682 683 684 685 686 687 688
    /**
     * Handshake (security context establishment process) between two Contexts
     * @param c the initiator
     * @param s the acceptor
     * @throws java.lang.Exception
     */
    static public void handshake(final Context c, final Context s) throws Exception {
        byte[] t = new byte[0];
W
weijun 已提交
689 690 691 692
        while (true) {
            if (t != null || !c.x.isEstablished()) t = c.take(t);
            if (t != null || !s.x.isEstablished()) t = s.take(t);
            if (c.x.isEstablished() && s.x.isEstablished()) break;
W
weijun 已提交
693 694 695
        }
    }
}