1 /*
2  * Copyright (c) 2005, 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 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 == GSSCaller.CALLER_SSL_CLIENT) {
113                 alts = new String[] {
114                     "com.sun.security.jgss.krb5.initiate",
115                     "com.sun.net.ssl.client",
116                 };
117             } else if (caller == GSSCaller.CALLER_SSL_SERVER) {
118                 alts = new String[] {
119                     "com.sun.security.jgss.krb5.accept",
120                     "com.sun.net.ssl.server",
121                 };
122             } else if (caller instanceof HttpCaller) {
123                 alts = new String[] {
124                     "com.sun.security.jgss.krb5.initiate",
125                 };
126             } else if (caller == GSSCaller.CALLER_UNKNOWN) {
127                 throw new AssertionError("caller not defined");
128             }
129         } else {
130             throw new IllegalArgumentException(mechName + " not supported");
131             // No other mech at the moment, maybe --
132             /*
133             switch (caller) {
134             case GSSUtil.CALLER_INITIATE:
135             case GSSUtil.CALLER_SSL_CLIENT:
136             case GSSUtil.CALLER_HTTP_NEGOTIATE:
137                 alts = new String[] {
138                     "com.sun.security.jgss." + mechName + ".initiate",
139                 };
140                 break;
141             case GSSUtil.CALLER_ACCEPT:
142             case GSSUtil.CALLER_SSL_SERVER:
143                 alts = new String[] {
144                     "com.sun.security.jgss." + mechName + ".accept",
145                 };
146                 break;
147             case GSSUtil.CALLER_UNKNOWN:
148                 // should never use
149                 throw new AssertionError("caller cannot be unknown");
150             default:
151                 throw new AssertionError("caller not defined");
152             }
153              */
154         }
155         for (String alt: alts) {
156             entries = config.getAppConfigurationEntry(alt);
157             if (debug != null) {
158                 debug.println("Trying " + alt +
159                         ((entries == null)?": does not exist.":": Found!"));
160             }
161             if (entries != null) {
162                 break;
163             }
164         }
165 
166         if (entries == null) {
167             if (debug != null) {
168                 debug.println("Cannot read JGSS entry, use default values instead.");
169             }
170             entries = getDefaultConfigurationEntry();
171         }
172         return entries;
173     }
174 
175     /**
176      * Default value for a caller-mech pair when no entry is defined in
177      * the system-wide Configuration object.
178      */
getDefaultConfigurationEntry()179     private AppConfigurationEntry[] getDefaultConfigurationEntry() {
180         HashMap <String, String> options = new HashMap <String, String> (2);
181 
182         if (mechName == null || mechName.equals("krb5")) {
183             if (isServerSide(caller)) {
184                 // Assuming the keytab file can be found through
185                 // krb5 config file or under user home directory
186                 options.put("useKeyTab", "true");
187                 options.put("storeKey", "true");
188                 options.put("doNotPrompt", "true");
189                 options.put("principal", "*");
190                 options.put("isInitiator", "false");
191             } else {
192                 if (caller instanceof HttpCaller && !HTTP_USE_GLOBAL_CREDS) {
193                     options.put("useTicketCache", "false");
194                 } else {
195                     options.put("useTicketCache", "true");
196                 }
197                 options.put("doNotPrompt", "false");
198             }
199             return new AppConfigurationEntry[] {
200                 new AppConfigurationEntry(
201                         "com.sun.security.auth.module.Krb5LoginModule",
202                         AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
203                         options)
204             };
205         }
206         return null;
207     }
208 
isServerSide(GSSCaller caller)209     private static boolean isServerSide (GSSCaller caller) {
210         return GSSCaller.CALLER_ACCEPT == caller ||
211                GSSCaller.CALLER_SSL_SERVER == caller;
212     }
213 }
214