1 /*
   2  * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.jgss.krb5;
  27 
  28 import org.ietf.jgss.*;
  29 import sun.security.jgss.GSSCaller;
  30 import sun.security.jgss.spi.*;
  31 import sun.security.krb5.*;
  32 import sun.security.krb5.Config;
  33 import javax.security.auth.kerberos.*;
  34 import java.net.InetAddress;
  35 import java.io.IOException;
  36 import java.util.Date;
  37 import java.security.AccessController;
  38 import java.security.AccessControlContext;
  39 import java.security.PrivilegedExceptionAction;
  40 import java.security.PrivilegedActionException;
  41 
  42 /**
  43  * Implements the krb5 initiator credential element.
  44  *
  45  * @author Mayank Upadhyay
  46  * @author Ram Marti
  47  * @since 1.4
  48  */
  49 
  50 public class Krb5InitCredential
  51     extends KerberosTicket
  52     implements Krb5CredElement {
  53 
  54     private static final long serialVersionUID = 7723415700837898232L;
  55 
  56     private Krb5NameElement name;
  57     private Credentials krb5Credentials;
  58 
  59     private Krb5InitCredential(Krb5NameElement name,
  60                                byte[] asn1Encoding,
  61                                KerberosPrincipal client,
  62                                KerberosPrincipal server,
  63                                byte[] sessionKey,
  64                                int keyType,
  65                                boolean[] flags,
  66                                Date authTime,
  67                                Date startTime,
  68                                Date endTime,
  69                                Date renewTill,
  70                                InetAddress[] clientAddresses)
  71                                throws GSSException {
  72         super(asn1Encoding,
  73               client,
  74               server,
  75               sessionKey,
  76               keyType,
  77               flags,
  78               authTime,
  79               startTime,
  80               endTime,
  81               renewTill,
  82               clientAddresses);
  83 
  84         this.name = name;
  85 
  86         try {
  87             // Cache this for later use by the sun.security.krb5 package.
  88             krb5Credentials = new Credentials(asn1Encoding,
  89                                               client.getName(),
  90                                               server.getName(),
  91                                               sessionKey,
  92                                               keyType,
  93                                               flags,
  94                                               authTime,
  95                                               startTime,
  96                                               endTime,
  97                                               renewTill,
  98                                               clientAddresses);
  99         } catch (KrbException e) {
 100             throw new GSSException(GSSException.NO_CRED, -1,
 101                                    e.getMessage());
 102         } catch (IOException e) {
 103             throw new GSSException(GSSException.NO_CRED, -1,
 104                                    e.getMessage());
 105         }
 106 
 107     }
 108 
 109     private Krb5InitCredential(Krb5NameElement name,
 110                                Credentials delegatedCred,
 111                                byte[] asn1Encoding,
 112                                KerberosPrincipal client,
 113                                KerberosPrincipal server,
 114                                byte[] sessionKey,
 115                                int keyType,
 116                                boolean[] flags,
 117                                Date authTime,
 118                                Date startTime,
 119                                Date endTime,
 120                                Date renewTill,
 121                                InetAddress[] clientAddresses)
 122                                throws GSSException {
 123         super(asn1Encoding,
 124               client,
 125               server,
 126               sessionKey,
 127               keyType,
 128               flags,
 129               authTime,
 130               startTime,
 131               endTime,
 132               renewTill,
 133               clientAddresses);
 134 
 135         this.name = name;
 136         // A delegated cred does not have all fields set. So do not try to
 137         // creat new Credentials out of the delegatedCred.
 138         this.krb5Credentials = delegatedCred;
 139     }
 140 
 141     static Krb5InitCredential getInstance(GSSCaller caller, Krb5NameElement name,
 142                                    int initLifetime)
 143         throws GSSException {
 144 
 145         KerberosTicket tgt = getTgt(caller, name, initLifetime);
 146         if (tgt == null)
 147             throw new GSSException(GSSException.NO_CRED, -1,
 148                                    "Failed to find any Kerberos tgt");
 149 
 150         if (name == null) {
 151             String fullName = tgt.getClient().getName();
 152             name = Krb5NameElement.getInstance(fullName,
 153                                        Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
 154         }
 155 
 156         return new Krb5InitCredential(name,
 157                                       tgt.getEncoded(),
 158                                       tgt.getClient(),
 159                                       tgt.getServer(),
 160                                       tgt.getSessionKey().getEncoded(),
 161                                       tgt.getSessionKeyType(),
 162                                       tgt.getFlags(),
 163                                       tgt.getAuthTime(),
 164                                       tgt.getStartTime(),
 165                                       tgt.getEndTime(),
 166                                       tgt.getRenewTill(),
 167                                       tgt.getClientAddresses());
 168     }
 169 
 170     static Krb5InitCredential getInstance(Krb5NameElement name,
 171                                    Credentials delegatedCred)
 172         throws GSSException {
 173 
 174         EncryptionKey sessionKey = delegatedCred.getSessionKey();
 175 
 176         /*
 177          * all of the following data is optional in a KRB-CRED
 178          * messages. This check for each field.
 179          */
 180 
 181         PrincipalName cPrinc = delegatedCred.getClient();
 182         PrincipalName sPrinc = delegatedCred.getServer();
 183 
 184         KerberosPrincipal client = null;
 185         KerberosPrincipal server = null;
 186 
 187         Krb5NameElement credName = null;
 188 
 189         if (cPrinc != null) {
 190             String fullName = cPrinc.getName();
 191             credName = Krb5NameElement.getInstance(fullName,
 192                                Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
 193             client =  new KerberosPrincipal(fullName);
 194         }
 195 
 196         // XXX Compare name to credName
 197 
 198         if (sPrinc != null) {
 199             server =
 200                 new KerberosPrincipal(sPrinc.getName(),
 201                                         KerberosPrincipal.KRB_NT_SRV_INST);
 202         }
 203 
 204         return new Krb5InitCredential(credName,
 205                                       delegatedCred,
 206                                       delegatedCred.getEncoded(),
 207                                       client,
 208                                       server,
 209                                       sessionKey.getBytes(),
 210                                       sessionKey.getEType(),
 211                                       delegatedCred.getFlags(),
 212                                       delegatedCred.getAuthTime(),
 213                                       delegatedCred.getStartTime(),
 214                                       delegatedCred.getEndTime(),
 215                                       delegatedCred.getRenewTill(),
 216                                       delegatedCred.getClientAddresses());
 217     }
 218 
 219     /**
 220      * Returns the principal name for this credential. The name
 221      * is in mechanism specific format.
 222      *
 223      * @return GSSNameSpi representing principal name of this credential
 224      * @exception GSSException may be thrown
 225      */
 226     public final GSSNameSpi getName() throws GSSException {
 227         return name;
 228     }
 229 
 230     /**
 231      * Returns the init lifetime remaining.
 232      *
 233      * @return the init lifetime remaining in seconds
 234      * @exception GSSException may be thrown
 235      */
 236     public int getInitLifetime() throws GSSException {
 237         int retVal = 0;
 238         Date d = getEndTime();
 239         if (d == null) {
 240             return 0;
 241         }
 242         retVal = (int)(d.getTime() - (new Date().getTime()));
 243 
 244         return retVal/1000;
 245     }
 246 
 247     /**
 248      * Returns the accept lifetime remaining.
 249      *
 250      * @return the accept lifetime remaining in seconds
 251      * @exception GSSException may be thrown
 252      */
 253     public int getAcceptLifetime() throws GSSException {
 254         return 0;
 255     }
 256 
 257     public boolean isInitiatorCredential() throws GSSException {
 258         return true;
 259     }
 260 
 261     public boolean isAcceptorCredential() throws GSSException {
 262         return false;
 263     }
 264 
 265     /**
 266      * Returns the oid representing the underlying credential
 267      * mechanism oid.
 268      *
 269      * @return the Oid for this credential mechanism
 270      * @exception GSSException may be thrown
 271      */
 272     public final Oid getMechanism() {
 273         return Krb5MechFactory.GSS_KRB5_MECH_OID;
 274     }
 275 
 276     public final java.security.Provider getProvider() {
 277         return Krb5MechFactory.PROVIDER;
 278     }
 279 
 280 
 281     /**
 282      * Returns a sun.security.krb5.Credentials instance so that it maybe
 283      * used in that package for th Kerberos protocol.
 284      */
 285     Credentials getKrb5Credentials() {
 286         return krb5Credentials;
 287     }
 288 
 289     /*
 290      * XXX Call to this.refresh() should refresh the locally cached copy
 291      * of krb5Credentials also.
 292      */
 293 
 294     /**
 295      * Called to invalidate this credential element.
 296      */
 297     public void dispose() throws GSSException {
 298         try {
 299             destroy();
 300         } catch (javax.security.auth.DestroyFailedException e) {
 301             GSSException gssException =
 302                 new GSSException(GSSException.FAILURE, -1,
 303                  "Could not destroy credentials - " + e.getMessage());
 304             gssException.initCause(e);
 305         }
 306     }
 307 
 308     // XXX call to this.destroy() should destroy the locally cached copy
 309     // of krb5Credentials and then call super.destroy().
 310 
 311     private static KerberosTicket getTgt(GSSCaller caller, Krb5NameElement name,
 312                                                  int initLifetime)
 313         throws GSSException {
 314 
 315         final String clientPrincipal;
 316 
 317         /*
 318          * Find the TGT for the realm that the client is in. If the client
 319          * name is not available, then use the default realm.
 320          */
 321         if (name != null) {
 322             clientPrincipal = (name.getKrb5PrincipalName()).getName();
 323         } else {
 324             clientPrincipal = null;
 325         }
 326 
 327         final AccessControlContext acc = AccessController.getContext();
 328 
 329         try {
 330             final GSSCaller realCaller = (caller == GSSCaller.CALLER_UNKNOWN)
 331                                    ? GSSCaller.CALLER_INITIATE
 332                                    : caller;
 333             return AccessController.doPrivileged(
 334                 new PrivilegedExceptionAction<KerberosTicket>() {
 335                 public KerberosTicket run() throws Exception {
 336                     // It's OK to use null as serverPrincipal. TGT is almost
 337                     // the first ticket for a principal and we use list.
 338                     return Krb5Util.getTicket(
 339                         realCaller,
 340                         clientPrincipal, null, acc);
 341                         }});
 342         } catch (PrivilegedActionException e) {
 343             GSSException ge =
 344                 new GSSException(GSSException.NO_CRED, -1,
 345                     "Attempt to obtain new INITIATE credentials failed!" +
 346                     " (" + e.getMessage() + ")");
 347             ge.initCause(e.getException());
 348             throw ge;
 349         }
 350     }
 351 
 352     @Override
 353     public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
 354         try {
 355             Krb5NameElement kname = (Krb5NameElement)name;
 356             Credentials newCred = Credentials.acquireS4U2selfCreds(
 357                     kname.getKrb5PrincipalName(), krb5Credentials);
 358             return new Krb5ProxyCredential(this, kname, newCred.getTicket());
 359         } catch (IOException | KrbException ke) {
 360             GSSException ge =
 361                 new GSSException(GSSException.FAILURE, -1,
 362                     "Attempt to obtain S4U2self credentials failed!");
 363             ge.initCause(ke);
 364             throw ge;
 365         }
 366     }
 367 }