1 /*
2  * Copyright (c) 2005, 2018, 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;
27 
28 import java.util.HashMap;
29 import javax.security.auth.login.AppConfigurationEntry;
30 import javax.security.auth.login.Configuration;
31 import org.ietf.jgss.Oid;
32 import sun.security.action.GetPropertyAction;
33 
34 /**
35  * A Configuration implementation especially designed for JGSS.
36  *
37  * @author weijun.wang
38  * @since 1.6
39  */
40 public class LoginConfigImpl extends Configuration {
41 
42     private final Configuration config;
43     private final GSSCaller caller;
44     private final String mechName;
45     private static final sun.security.util.Debug debug =
46         sun.security.util.Debug.getInstance("gssloginconfig", "\t[GSS LoginConfigImpl]");
47 
48     public static final boolean HTTP_USE_GLOBAL_CREDS;
49 
50     static {
51         String prop = GetPropertyAction
52                 .privilegedGetProperty("http.use.global.creds");
53         //HTTP_USE_GLOBAL_CREDS = "true".equalsIgnoreCase(prop); // default false
54         HTTP_USE_GLOBAL_CREDS = !"false".equalsIgnoreCase(prop); // default true
55     }
56 
57 
58     /**
59      * A new instance of LoginConfigImpl must be created for each login request
60      * since it's only used by a single (caller, mech) pair
61      * @param caller defined in GSSUtil as CALLER_XXX final fields
62      * @param mech defined in GSSUtil as XXX_MECH_OID final fields
63      */
LoginConfigImpl(GSSCaller caller, Oid mech)64     public LoginConfigImpl(GSSCaller caller, Oid mech) {
65 
66         this.caller = caller;
67 
68         if (mech.equals(GSSUtil.GSS_KRB5_MECH_OID)) {
69             mechName = "krb5";
70         } else {
71             throw new IllegalArgumentException(mech.toString() + " not supported");
72         }
73         config = java.security.AccessController.doPrivileged
74                 (new java.security.PrivilegedAction <Configuration> () {
75             public Configuration run() {
76                 return Configuration.getConfiguration();
77             }
78         });
79     }
80 
81     /**
82      * @param name Almost useless, since the (caller, mech) is already passed
83      *             into constructor. The only use will be detecting OTHER which
84      *             is called in LoginContext
85      */
getAppConfigurationEntry(String name)86     public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
87 
88         AppConfigurationEntry[] entries = null;
89 
90         // This is the second call from LoginContext, which we will just ignore
91         if ("OTHER".equalsIgnoreCase(name)) {
92             return null;
93         }
94 
95         String[] alts = null;
96 
97         // Compatibility:
98         // For the 4 old callers, old entry names will be used if the new
99         // entry name is not provided.
100 
101         if ("krb5".equals(mechName)) {
102             if (caller == GSSCaller.CALLER_INITIATE) {
103                 alts = new String[] {
104                     "com.sun.security.jgss.krb5.initiate",
105                     "com.sun.security.jgss.initiate",
106                 };
107             } else if (caller == GSSCaller.CALLER_ACCEPT) {
108                 alts = new String[] {
109                     "com.sun.security.jgss.krb5.accept",
110                     "com.sun.security.jgss.accept",
111                 };
112             } else if (caller instanceof HttpCaller) {
113                 alts = new String[] {
114                     "com.sun.security.jgss.krb5.initiate",
115                 };
116             } else if (caller == GSSCaller.CALLER_UNKNOWN) {
117                 throw new AssertionError("caller not defined");
118             }
119         } else {
120             throw new IllegalArgumentException(mechName + " not supported");
121             // No other mech at the moment, maybe --
122             /*
123             switch (caller) {
124             case GSSUtil.CALLER_INITIATE:
125             case GSSUtil.CALLER_HTTP_NEGOTIATE:
126                 alts = new String[] {
127                     "com.sun.security.jgss." + mechName + ".initiate",
128                 };
129                 break;
130             case GSSUtil.CALLER_ACCEPT:
131                 alts = new String[] {
132                     "com.sun.security.jgss." + mechName + ".accept",
133                 };
134                 break;
135             case GSSUtil.CALLER_UNKNOWN:
136                 // should never use
137                 throw new AssertionError("caller cannot be unknown");
138             default:
139                 throw new AssertionError("caller not defined");
140             }
141              */
142         }
143         for (String alt: alts) {
144             entries = config.getAppConfigurationEntry(alt);
145             if (debug != null) {
146                 debug.println("Trying " + alt +
147                         ((entries == null)?": does not exist.":": Found!"));
148             }
149             if (entries != null) {
150                 break;
151             }
152         }
153 
154         if (entries == null) {
155             if (debug != null) {
156                 debug.println("Cannot read JGSS entry, use default values instead.");
157             }
158             entries = getDefaultConfigurationEntry();
159         }
160         return entries;
161     }
162 
163     /**
164      * Default value for a caller-mech pair when no entry is defined in
165      * the system-wide Configuration object.
166      */
getDefaultConfigurationEntry()167     private AppConfigurationEntry[] getDefaultConfigurationEntry() {
168         HashMap <String, String> options = new HashMap <String, String> (2);
169 
170         if (mechName == null || mechName.equals("krb5")) {
171             if (isServerSide(caller)) {
172                 // Assuming the keytab file can be found through
173                 // krb5 config file or under user home directory
174                 options.put("useKeyTab", "true");
175                 options.put("storeKey", "true");
176                 options.put("doNotPrompt", "true");
177                 options.put("principal", "*");
178                 options.put("isInitiator", "false");
179             } else {
180                 if (caller instanceof HttpCaller && !HTTP_USE_GLOBAL_CREDS) {
181                     options.put("useTicketCache", "false");
182                 } else {
183                     options.put("useTicketCache", "true");
184                 }
185                 options.put("doNotPrompt", "false");
186             }
187             return new AppConfigurationEntry[] {
188                 new AppConfigurationEntry(
189                         "com.sun.security.auth.module.Krb5LoginModule",
190                         AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
191                         options)
192             };
193         }
194         return null;
195     }
196 
isServerSide(GSSCaller caller)197     private static boolean isServerSide (GSSCaller caller) {
198         return GSSCaller.CALLER_ACCEPT == caller;
199     }
200 }
201