1 /*
   2  * Copyright (c) 2000, 2017, 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 /*
  27  *  (C) Copyright IBM Corp. 1999 All Rights Reserved.
  28  *  Copyright 1997 The Open Group Research Institute.  All rights reserved.
  29  */
  30 
  31 package sun.security.krb5.internal.tools;
  32 
  33 import sun.security.krb5.*;
  34 import sun.security.krb5.internal.*;
  35 import sun.security.krb5.internal.ccache.*;
  36 import java.io.IOException;
  37 import java.time.Instant;
  38 import java.io.FileInputStream;
  39 
  40 /**
  41  * Maintains user-specific options or default settings when the user requests
  42  * a KDC ticket using Kinit.
  43  *
  44  * @author Yanni Zhang
  45  * @author Ram Marti
  46  */
  47 class KinitOptions {
  48 
  49     // 1. acquire, 2. renew, 3. validate
  50     public int action = 1;
  51 
  52     // forwardable and proxiable flags have two states:
  53     // -1 - flag set to be not forwardable or proxiable;
  54     // 1 - flag set to be forwardable or proxiable.
  55     public short forwardable = 0;
  56     public short proxiable = 0;
  57     public KerberosTime lifetime;
  58     public KerberosTime renewable_lifetime;
  59     public String target_service;
  60     public String keytab_file;
  61     public String cachename;
  62     private PrincipalName principal;
  63     public String realm;
  64     char[] password = null;
  65     public boolean keytab;
  66     private boolean DEBUG = Krb5.DEBUG;
  67     private boolean includeAddresses = true; // default.
  68     private boolean useKeytab = false; // default = false.
  69     private String ktabName; // keytab file name
  70 
  71     public KinitOptions() throws RuntimeException, RealmException {
  72         // no args were specified in the command line;
  73         // use default values
  74         cachename = FileCredentialsCache.getDefaultCacheName();
  75         if (cachename == null) {
  76             throw new RuntimeException("default cache name error");
  77         }
  78         principal = getDefaultPrincipal();
  79     }
  80 
  81     public void setKDCRealm(String r) throws RealmException {
  82         realm = r;
  83     }
  84 
  85     public String getKDCRealm() {
  86         if (realm == null) {
  87             if (principal != null) {
  88                 return principal.getRealmString();
  89             }
  90         }
  91         return null;
  92     }
  93 
  94     public KinitOptions(String[] args)
  95         throws KrbException, RuntimeException, IOException {
  96         // currently we provide support for -f -p -c principal options
  97         String p = null; // store principal
  98 
  99         for (int i = 0; i < args.length; i++) {
 100             if (args[i].equals("-f")) {
 101                 forwardable = 1;
 102             } else if (args[i].equals("-p")) {
 103                 proxiable = 1;
 104             } else if (args[i].equals("-c")) {
 105 
 106                 if (args[i + 1].startsWith("-")) {
 107                     throw new IllegalArgumentException("input format " +
 108                                                        " not correct: " +
 109                                                        " -c  option " +
 110                                                        "must be followed " +
 111                                                        "by the cache name");
 112                 }
 113                 cachename = args[++i];
 114                 if ((cachename.length() >= 5) &&
 115                     cachename.substring(0, 5).equalsIgnoreCase("FILE:")) {
 116                     cachename = cachename.substring(5);
 117                 };
 118             } else if (args[i].equals("-A")) {
 119                 includeAddresses = false;
 120             } else if (args[i].equals("-k")) {
 121                 useKeytab = true;
 122             } else if (args[i].equals("-t")) {
 123                 if (ktabName != null) {
 124                     throw new IllegalArgumentException
 125                         ("-t option/keytab file name repeated");
 126                 } else if (i + 1 < args.length) {
 127                     ktabName = args[++i];
 128                 } else {
 129                     throw new IllegalArgumentException
 130                         ("-t option requires keytab file name");
 131                 }
 132 
 133                 useKeytab = true;
 134             } else if (args[i].equals("-R")) {
 135                 action = 2;
 136             } else if (args[i].equals("-l")) {
 137                 lifetime = getTime(Config.duration(args[++i]));
 138             } else if (args[i].equals("-r")) {
 139                 renewable_lifetime = getTime(Config.duration(args[++i]));
 140             } else if (args[i].equalsIgnoreCase("-?") ||
 141                        args[i].equalsIgnoreCase("-h") ||
 142                        args[i].equalsIgnoreCase("--help") ||
 143                        // -help: legacy.
 144                        args[i].equalsIgnoreCase("-help")) {
 145                 printHelp();
 146                 System.exit(0);
 147             } else if (p == null) { // Haven't yet processed a "principal"
 148                 p = args[i];
 149                 try {
 150                     principal = new PrincipalName(p);
 151                 } catch (Exception e) {
 152                     throw new IllegalArgumentException("invalid " +
 153                                                        "Principal name: " + p +
 154                                                        e.getMessage());
 155                 }
 156             } else if (this.password == null) {
 157                 // Have already processed a Principal, this must be a password
 158                 password = args[i].toCharArray();
 159             } else {
 160                 throw new IllegalArgumentException("too many parameters");
 161             }
 162         }
 163         // we should get cache name before getting the default principal name
 164         if (cachename == null) {
 165             cachename = FileCredentialsCache.getDefaultCacheName();
 166             if (cachename == null) {
 167                 throw new RuntimeException("default cache name error");
 168             }
 169         }
 170         if (principal == null) {
 171             principal = getDefaultPrincipal();
 172         }
 173     }
 174 
 175     PrincipalName getDefaultPrincipal() {
 176 
 177         // get default principal name from the cachename if it is
 178         // available.
 179 
 180         try {
 181             CCacheInputStream cis =
 182                 new CCacheInputStream(new FileInputStream(cachename));
 183             int version;
 184             if ((version = cis.readVersion()) ==
 185                     FileCCacheConstants.KRB5_FCC_FVNO_4) {
 186                 cis.readTag();
 187             } else {
 188                 if (version == FileCCacheConstants.KRB5_FCC_FVNO_1 ||
 189                         version == FileCCacheConstants.KRB5_FCC_FVNO_2) {
 190                     cis.setNativeByteOrder();
 191                 }
 192             }
 193             PrincipalName p = cis.readPrincipal(version);
 194             cis.close();
 195             if (DEBUG) {
 196                 System.out.println(">>>KinitOptions principal name from "+
 197                                    "the cache is :" + p);
 198             }
 199             return p;
 200         } catch (IOException e) {
 201             // ignore any exceptions; we will use the user name as the
 202             // principal name
 203             if (DEBUG) {
 204                 e.printStackTrace();
 205             }
 206         } catch (RealmException e) {
 207             if (DEBUG) {
 208                 e.printStackTrace();
 209             }
 210         }
 211 
 212         String username = System.getProperty("user.name");
 213         if (DEBUG) {
 214             System.out.println(">>>KinitOptions default username is :"
 215                                + username);
 216         }
 217         try {
 218             PrincipalName p = new PrincipalName(username);
 219             return p;
 220         } catch (RealmException e) {
 221             // ignore exception , return null
 222             if (DEBUG) {
 223                 System.out.println ("Exception in getting principal " +
 224                                     "name " + e.getMessage());
 225                 e.printStackTrace();
 226             }
 227         }
 228         return null;
 229     }
 230 
 231 
 232     void printHelp() {
 233         System.out.println("Usage:\n\n1. Initial ticket request:\n" +
 234                 "    kinit [-A] [-f] [-p] [-c cachename] " +
 235                 "[-l lifetime] [-r renewable_time]\n" +
 236                 "          [[-k [-t keytab_file_name]] [principal] " +
 237                            "[password]");
 238         System.out.println("2. Renew a ticket:\n" +
 239                 "    kinit -R [-c cachename] [principal]");
 240         System.out.println("\nAvailable options to " +
 241                            "Kerberos 5 ticket request:");
 242         System.out.println("\t-A   do not include addresses");
 243         System.out.println("\t-f   forwardable");
 244         System.out.println("\t-p   proxiable");
 245         System.out.println("\t-c   cache name " +
 246                 "(i.e., FILE:\\d:\\myProfiles\\mykrb5cache)");
 247         System.out.println("\t-l   lifetime");
 248         System.out.println("\t-r   renewable time " +
 249                 "(total lifetime a ticket can be renewed)");
 250         System.out.println("\t-k   use keytab");
 251         System.out.println("\t-t   keytab file name");
 252         System.out.println("\tprincipal   the principal name "+
 253                 "(i.e., qweadf@ATHENA.MIT.EDU qweadf)");
 254         System.out.println("\tpassword    the principal's Kerberos password");
 255     }
 256 
 257     public boolean getAddressOption() {
 258         return includeAddresses;
 259     }
 260 
 261     public boolean useKeytabFile() {
 262         return useKeytab;
 263     }
 264 
 265     public String keytabFileName() {
 266         return ktabName;
 267     }
 268 
 269     public PrincipalName getPrincipal() {
 270         return principal;
 271     }
 272 
 273     private KerberosTime getTime(int s) {
 274         return new KerberosTime(Instant.now().plusSeconds(s));
 275     }
 276 }