1 /*
2  * Copyright (c) 2013, 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.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @test
26  * @bug 8009977 8186884 8201627
27  * @summary A test to launch multiple Java processes using either Java GSS
28  *          or native GSS
29  * @library ../../../../java/security/testlibrary /lib/testlibrary
30  * @compile -XDignore.symbol.file BasicProc.java
31  * @run main/othervm -Dsun.net.spi.nameservice.provider.1=ns,mock BasicProc launcher
32  */
33 
34 import java.nio.file.Files;
35 import java.nio.file.Paths;
36 import java.nio.file.attribute.PosixFilePermission;
37 import java.util.Arrays;
38 import java.util.Collections;
39 import java.util.HashSet;
40 import java.util.PropertyPermission;
41 import java.util.Set;
42 
43 import jdk.testlibrary.Asserts;
44 import org.ietf.jgss.Oid;
45 import sun.security.krb5.Config;
46 
47 import javax.security.auth.PrivateCredentialPermission;
48 
49 /**
50  * Run this test automatically and test Java GSS with embedded KDC.
51  *
52  * Run with customized native.krb5.libs to test interop between Java GSS
53  * and native GSS, and native.kdc.path with a native KDC. For example,
54  * run the following command to test interop among Java, default native,
55  * MIT, and Heimdal krb5 libraries with the Heimdal KDC:
56  *
57  *    jtreg -Dnative.krb5.libs=j=,
58  *                             n=,
59  *                             k=/usr/local/krb5/lib/libgssapi_krb5.so,
60  *                             h=/space/install/heimdal/lib/libgssapi.so \
61  *          -Dnative.kdc.path=/usr/local/heimdal \
62  *          BasicProc.java
63  *
64  * Note: The first 4 lines should be concatenated to make a long system
65  * property value with no blank around ",". This comma-separated value
66  * has each element being name=libpath. The special name "j" means the
67  * Java library and libpath is ignored. Otherwise it means a native library,
68  * and libpath (can be empty) will be the value for the sun.security.jgss.lib
69  * system property. If this system property is not set, only the Java
70  * library will be tested.
71  */
72 
73 public class BasicProc {
74 
75     private static final String CONF = "krb5.conf";
76     private static final String KTAB_S = "server.ktab";
77     private static final String KTAB_B = "backend.ktab";
78 
79     private static final String HOST = "localhost";
80     private static final String SERVER = "server/" + HOST;
81     private static final String BACKEND = "backend/" + HOST;
82     private static final String USER = "user";
83     private static final char[] PASS = "password".toCharArray();
84     private static final String REALM = "REALM";
85 
86     private static final int MSGSIZE = 1024;
87     private static final byte[] MSG = new byte[MSGSIZE];
88 
main(String[] args)89     public static void main(String[] args) throws Exception {
90 
91         Oid oid = new Oid("1.2.840.113554.1.2.2");
92         byte[] token, msg;
93 
94         switch (args[0]) {
95             case "launcher":
96                 KDC kdc = KDC.create(REALM, HOST, 0, true);
97                 try {
98                     kdc.addPrincipal(USER, PASS);
99                     kdc.addPrincipalRandKey("krbtgt/" + REALM);
100                     kdc.addPrincipalRandKey(SERVER);
101                     kdc.addPrincipalRandKey(BACKEND);
102 
103                     // Native lib might do some name lookup
104                     KDC.saveConfig(CONF, kdc,
105                             "dns_lookup_kdc = no",
106                             "ticket_lifetime = 1h",
107                             "dns_lookup_realm = no",
108                             "dns_canonicalize_hostname = false",
109                             "forwardable = true");
110                     System.setProperty("java.security.krb5.conf", CONF);
111                     Config.refresh();
112                     kdc.writeKtab(KTAB_S, false, SERVER);
113                     kdc.writeKtab(KTAB_B, false, BACKEND);
114 
115                     String[] tmp = System.getProperty("native.krb5.libs", "j=")
116                             .split(",");
117 
118                     // Library paths. The 1st one is always null which means
119                     // Java, "" means the default native lib.
120                     String[] libs = new String[tmp.length];
121 
122                     // Names for each lib above. Use in file names.
123                     String[] names = new String[tmp.length];
124 
125                     boolean hasNative = false;
126 
127                     for (int i = 0; i < tmp.length; i++) {
128                         if (tmp[i].isEmpty()) {
129                             throw new Exception("Invalid native.krb5.libs");
130                         }
131                         String[] pair = tmp[i].split("=", 2);
132                         names[i] = pair[0];
133                         if (!pair[0].equals("j")) {
134                             libs[i] = pair.length > 1 ? pair[1] : "";
135                             hasNative = true;
136                         }
137                     }
138 
139                     if (hasNative) {
140                         kdc.kinit(USER, "base.ccache");
141                     }
142 
143                     // Try the same lib first
144                     for (int i = 0; i < libs.length; i++) {
145                         once(names[i] + names[i] + names[i],
146                                 libs[i], libs[i], libs[i]);
147                     }
148 
149                     for (int i = 0; i < libs.length; i++) {
150                         for (int j = 0; j < libs.length; j++) {
151                             for (int k = 0; k < libs.length; k++) {
152                                 if (i != j || i != k) {
153                                     once(names[i] + names[j] + names[k],
154                                             libs[i], libs[j], libs[k]);
155                                 }
156                             }
157                         }
158                     }
159                 } finally {
160                     kdc.terminate();
161                 }
162                 break;
163             case "client":
164                 Context c = args[1].equals("n") ?
165                         Context.fromThinAir() :
166                         Context.fromUserPass(USER, PASS, false);
167                 c.startAsClient(SERVER, oid);
168                 c.x().requestCredDeleg(true);
169                 c.x().requestMutualAuth(true);
170                 Proc.binOut(c.take(new byte[0])); // AP-REQ
171                 c.take(Proc.binIn()); // AP-REP
172                 Proc.binOut(c.wrap(MSG, true));
173                 Proc.binOut(c.getMic(MSG));
174                 break;
175             case "server":
176                 Context s = args[1].equals("n") ?
177                         Context.fromThinAir() :
178                         Context.fromUserKtab(SERVER, KTAB_S, true);
179                 s.startAsServer(oid);
180                 token = Proc.binIn(); // AP-REQ
181                 Proc.binOut(s.take(token)); // AP-REP
182                 msg = s.unwrap(Proc.binIn(), true);
183                 Asserts.assertTrue(Arrays.equals(msg, MSG));
184                 s.verifyMic(Proc.binIn(), msg);
185                 Context s2 = s.delegated();
186                 s2.startAsClient(BACKEND, oid);
187                 s2.x().requestMutualAuth(false);
188                 Proc.binOut(s2.take(new byte[0])); // AP-REQ
189                 msg = s2.unwrap(Proc.binIn(), true);
190                 Asserts.assertTrue(Arrays.equals(msg, MSG));
191                 s2.verifyMic(Proc.binIn(), msg);
192                 break;
193             case "backend":
194                 Context b = args[1].equals("n") ?
195                         Context.fromThinAir() :
196                         Context.fromUserKtab(BACKEND, KTAB_B, true);
197                 b.startAsServer(oid);
198                 token = b.take(Proc.binIn()); // AP-REQ
199                 Asserts.assertTrue(token == null);
200                 Proc.binOut(b.wrap(MSG, true));
201                 Proc.binOut(b.getMic(MSG));
202                 break;
203         }
204     }
205 
206     /**
207      * One test run.
208      *
209      * @param label test label
210      * @param lc lib of client
211      * @param ls lib of server
212      * @param lb lib of backend
213      */
once(String label, String lc, String ls, String lb)214     private static void once(String label, String lc, String ls, String lb)
215             throws Exception {
216 
217         Proc pc = proc(lc)
218                 .args("client", lc == null ? "j" : "n")
219                 .perm(new javax.security.auth.kerberos.ServicePermission(
220                         "krbtgt/" + REALM + "@" + REALM, "initiate"))
221                 .perm(new javax.security.auth.kerberos.ServicePermission(
222                         SERVER + "@" + REALM, "initiate"))
223                 .perm(new javax.security.auth.kerberos.DelegationPermission(
224                         "\"" + SERVER + "@" + REALM + "\" " +
225                                 "\"krbtgt/" + REALM + "@" + REALM + "\""))
226                 .debug(label + "-C");
227         if (lc == null) {
228             // for Krb5LoginModule::promptForName
229             pc.perm(new PropertyPermission("user.name", "read"));
230         } else {
231             Files.copy(Paths.get("base.ccache"), Paths.get(label + ".ccache"));
232             Set<PosixFilePermission> perms = new HashSet<>();
233             perms.add(PosixFilePermission.OWNER_READ);
234             perms.add(PosixFilePermission.OWNER_WRITE);
235             Files.setPosixFilePermissions(Paths.get(label + ".ccache"),
236                                           Collections.unmodifiableSet(perms));
237             pc.env("KRB5CCNAME", label + ".ccache");
238             // Do not try system ktab if ccache fails
239             pc.env("KRB5_KTNAME", "none");
240         }
241         pc.start();
242 
243         Proc ps = proc(ls)
244                 .args("server", ls == null ? "j" : "n")
245                 .perm(new javax.security.auth.kerberos.ServicePermission(
246                         SERVER + "@" + REALM, "accept"))
247                 .perm(new javax.security.auth.kerberos.ServicePermission(
248                         BACKEND + "@" + REALM, "initiate"))
249                 .debug(label + "-S");
250         if (ls == null) {
251             ps.perm(new PrivateCredentialPermission(
252                     "javax.security.auth.kerberos.KeyTab * \"*\"", "read"))
253                 .perm(new java.io.FilePermission(KTAB_S, "read"));
254         } else {
255             ps.env("KRB5_KTNAME", KTAB_S);
256         }
257         ps.start();
258 
259         Proc pb = proc(lb)
260                 .args("backend", lb == null ? "j" : "n")
261                 .perm(new javax.security.auth.kerberos.ServicePermission(
262                         BACKEND + "@" + REALM, "accept"))
263                 .debug(label + "-B");
264         if (lb == null) {
265             pb.perm(new PrivateCredentialPermission(
266                     "javax.security.auth.kerberos.KeyTab * \"*\"", "read"))
267                 .perm(new java.io.FilePermission(KTAB_B, "read"));
268         } else {
269             pb.env("KRB5_KTNAME", KTAB_B);
270         }
271         pb.start();
272 
273         // Client and server
274         ps.println(pc.readData()); // AP-REQ
275         pc.println(ps.readData()); // AP-REP
276 
277         ps.println(pc.readData()); // KRB-PRIV
278         ps.println(pc.readData()); // KRB-SAFE
279 
280         // Server and backend
281         pb.println(ps.readData()); // AP-REQ
282 
283         ps.println(pb.readData()); // KRB-PRIV
284         ps.println(pb.readData()); // KRB-SAFE
285 
286         if ((pc.waitFor() | ps.waitFor() | pb.waitFor()) != 0) {
287             throw new Exception("Process failed");
288         }
289     }
290 
291     /**
292      * A Proc for a child process.
293      *
294      * @param lib the library. Null is Java. "" is default native lib.
295      */
proc(String lib)296     private static Proc proc(String lib) throws Exception {
297         Proc p = Proc.create("BasicProc")
298                 .prop("java.security.manager", "")
299                 .prop("sun.net.spi.nameservice.provider.1", "ns,mock")
300                 .perm(new javax.security.auth.AuthPermission("doAs"));
301         if (lib != null) {
302             p.env("KRB5_CONFIG", CONF)
303                     .env("KRB5_TRACE", "/dev/stderr")
304                     .prop("sun.security.jgss.native", "true")
305                     .prop("sun.security.jgss.lib", lib)
306                     .prop("javax.security.auth.useSubjectCredsOnly", "false")
307                     .prop("sun.security.nativegss.debug", "true");
308             int pos = lib.lastIndexOf('/');
309             if (pos > 0) {
310                 p.env("LD_LIBRARY_PATH", lib.substring(0, pos));
311                 p.env("DYLD_LIBRARY_PATH", lib.substring(0, pos));
312             }
313         } else {
314             p.perm(new java.util.PropertyPermission(
315                             "sun.security.krb5.principal", "read"))
316                             // For Krb5LoginModule::login.
317                     .perm(new java.lang.RuntimePermission(
318                             "accessClassInPackage.sun.net.spi.nameservice"))
319                     .perm(new javax.security.auth.AuthPermission(
320                             "modifyPrincipals"))
321                     .perm(new javax.security.auth.AuthPermission(
322                             "modifyPrivateCredentials"))
323                     .prop("sun.security.krb5.debug", "true")
324                     .prop("java.security.krb5.conf", CONF);
325         }
326         return p;
327     }
328 }
329