View Javadoc
1   /*
2    * Copyright (c) 2005, 2009, 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.wrapper;
27  
28  import org.ietf.jgss.*;
29  import java.security.Provider;
30  import sun.security.jgss.GSSHeader;
31  import sun.security.jgss.GSSUtil;
32  import sun.security.jgss.GSSExceptionImpl;
33  import sun.security.jgss.spi.*;
34  import sun.security.util.DerValue;
35  import sun.security.util.ObjectIdentifier;
36  import sun.security.jgss.spnego.NegTokenInit;
37  import sun.security.jgss.spnego.NegTokenTarg;
38  import javax.security.auth.kerberos.DelegationPermission;
39  import com.sun.security.jgss.InquireType;
40  import java.io.*;
41  
42  
43  /**
44   * This class is essentially a wrapper class for the gss_ctx_id_t
45   * structure of the native GSS library.
46   * @author Valerie Peng
47   * @since 1.6
48   */
49  class NativeGSSContext implements GSSContextSpi {
50  
51      private static final int GSS_C_DELEG_FLAG = 1;
52      private static final int GSS_C_MUTUAL_FLAG = 2;
53      private static final int GSS_C_REPLAY_FLAG = 4;
54      private static final int GSS_C_SEQUENCE_FLAG = 8;
55      private static final int GSS_C_CONF_FLAG = 16;
56      private static final int GSS_C_INTEG_FLAG = 32;
57      private static final int GSS_C_ANON_FLAG = 64;
58      private static final int GSS_C_PROT_READY_FLAG = 128;
59      private static final int GSS_C_TRANS_FLAG = 256;
60  
61      private static final int NUM_OF_INQUIRE_VALUES = 6;
62  
63      private long pContext = 0; // Pointer to the gss_ctx_id_t structure
64      private GSSNameElement srcName;
65      private GSSNameElement targetName;
66      private GSSCredElement cred;
67      private boolean isInitiator;
68      private boolean isEstablished;
69      private Oid actualMech; // Assigned during context establishment
70  
71      private ChannelBinding cb;
72      private GSSCredElement delegatedCred;
73      private int flags;
74      private int lifetime = GSSCredential.DEFAULT_LIFETIME;
75      private final GSSLibStub cStub;
76  
77      private boolean skipDelegPermCheck;
78      private boolean skipServicePermCheck;
79  
80      // Retrieve the (preferred) mech out of SPNEGO tokens, i.e.
81      // NegTokenInit & NegTokenTarg
82      private static Oid getMechFromSpNegoToken(byte[] token,
83                                                boolean isInitiator)
84          throws GSSException {
85          Oid mech = null;
86          if (isInitiator) {
87              GSSHeader header = null;
88              try {
89                  header = new GSSHeader(new ByteArrayInputStream(token));
90              } catch (IOException ioe) {
91                  throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
92              }
93              int negTokenLen = header.getMechTokenLength();
94              byte[] negToken = new byte[negTokenLen];
95              System.arraycopy(token, token.length-negTokenLen,
96                               negToken, 0, negToken.length);
97  
98              NegTokenInit ntok = new NegTokenInit(negToken);
99              if (ntok.getMechToken() != null) {
100                 Oid[] mechList = ntok.getMechTypeList();
101                 mech = mechList[0];
102             }
103         } else {
104             NegTokenTarg ntok = new NegTokenTarg(token);
105             mech = ntok.getSupportedMech();
106         }
107         return mech;
108     }
109 
110     // Perform the Service permission check
111     private void doServicePermCheck() throws GSSException {
112         if (System.getSecurityManager() != null) {
113             String action = (isInitiator? "initiate" : "accept");
114             // Need to check Service permission for accessing
115             // initiator cred for SPNEGO during context establishment
116             if (GSSUtil.isSpNegoMech(cStub.getMech()) && isInitiator
117                 && !isEstablished) {
118                 if (srcName == null) {
119                     // Check by creating default initiator KRB5 cred
120                     GSSCredElement tempCred =
121                         new GSSCredElement(null, lifetime,
122                                            GSSCredential.INITIATE_ONLY,
123                                            GSSLibStub.getInstance(GSSUtil.GSS_KRB5_MECH_OID));
124                     tempCred.dispose();
125                 } else {
126                     String tgsName = Krb5Util.getTGSName(srcName);
127                     Krb5Util.checkServicePermission(tgsName, action);
128                 }
129             }
130             String targetStr = targetName.getKrbName();
131             Krb5Util.checkServicePermission(targetStr, action);
132             skipServicePermCheck = true;
133         }
134     }
135 
136     // Perform the Delegation permission check
137     private void doDelegPermCheck() throws GSSException {
138         SecurityManager sm = System.getSecurityManager();
139         if (sm != null) {
140             String targetStr = targetName.getKrbName();
141             String tgsStr = Krb5Util.getTGSName(targetName);
142             StringBuffer buf = new StringBuffer("\"");
143             buf.append(targetStr).append("\" \"");
144             buf.append(tgsStr).append('\"');
145             String krbPrincPair = buf.toString();
146             SunNativeProvider.debug("Checking DelegationPermission (" +
147                                     krbPrincPair + ")");
148             DelegationPermission perm =
149                 new DelegationPermission(krbPrincPair);
150             sm.checkPermission(perm);
151             skipDelegPermCheck = true;
152         }
153     }
154 
155     private byte[] retrieveToken(InputStream is, int mechTokenLen)
156         throws GSSException {
157         try {
158             byte[] result = null;
159             if (mechTokenLen != -1) {
160                 // Need to add back the GSS header for a complete GSS token
161                 SunNativeProvider.debug("Precomputed mechToken length: " +
162                                          mechTokenLen);
163                 GSSHeader gssHeader = new GSSHeader
164                     (new ObjectIdentifier(cStub.getMech().toString()),
165                      mechTokenLen);
166                 ByteArrayOutputStream baos = new ByteArrayOutputStream(600);
167 
168                 byte[] mechToken = new byte[mechTokenLen];
169                 int len = is.read(mechToken);
170                 assert(mechTokenLen == len);
171                 gssHeader.encode(baos);
172                 baos.write(mechToken);
173                 result = baos.toByteArray();
174             } else {
175                 // Must be unparsed GSS token or SPNEGO's NegTokenTarg token
176                 assert(mechTokenLen == -1);
177                 DerValue dv = new DerValue(is);
178                 result = dv.toByteArray();
179             }
180             SunNativeProvider.debug("Complete Token length: " +
181                                     result.length);
182             return result;
183         } catch (IOException ioe) {
184             throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
185         }
186     }
187 
188     // Constructor for context initiator
189     NativeGSSContext(GSSNameElement peer, GSSCredElement myCred,
190                      int time, GSSLibStub stub) throws GSSException {
191         if (peer == null) {
192             throw new GSSException(GSSException.FAILURE, 1, "null peer");
193         }
194         cStub = stub;
195         cred = myCred;
196         targetName = peer;
197         isInitiator = true;
198         lifetime = time;
199 
200         if (GSSUtil.isKerberosMech(cStub.getMech())) {
201             doServicePermCheck();
202             if (cred == null) {
203                 cred = new GSSCredElement(null, lifetime,
204                                           GSSCredential.INITIATE_ONLY, cStub);
205             }
206             srcName = cred.getName();
207         }
208     }
209 
210     // Constructor for context acceptor
211     NativeGSSContext(GSSCredElement myCred, GSSLibStub stub)
212         throws GSSException {
213         cStub = stub;
214         cred = myCred;
215 
216         if (cred != null) targetName = cred.getName();
217 
218         isInitiator = false;
219         // Defer Service permission check for default acceptor cred
220         // to acceptSecContext()
221         if (GSSUtil.isKerberosMech(cStub.getMech()) && targetName != null) {
222             doServicePermCheck();
223         }
224 
225         // srcName and potentially targetName (when myCred is null)
226         // will be set in GSSLibStub.acceptContext(...)
227     }
228 
229     // Constructor for imported context
230     NativeGSSContext(long pCtxt, GSSLibStub stub) throws GSSException {
231         assert(pContext != 0);
232         pContext = pCtxt;
233         cStub = stub;
234 
235         // Set everything except cred, cb, delegatedCred
236         long[] info = cStub.inquireContext(pContext);
237         if (info.length != NUM_OF_INQUIRE_VALUES) {
238             throw new RuntimeException("Bug w/ GSSLibStub.inquireContext()");
239         }
240         srcName = new GSSNameElement(info[0], cStub);
241         targetName = new GSSNameElement(info[1], cStub);
242         isInitiator = (info[2] != 0);
243         isEstablished = (info[3] != 0);
244         flags = (int) info[4];
245         lifetime = (int) info[5];
246 
247         // Do Service Permission check when importing SPNEGO context
248         // just to be safe
249         Oid mech = cStub.getMech();
250         if (GSSUtil.isSpNegoMech(mech) || GSSUtil.isKerberosMech(mech)) {
251             doServicePermCheck();
252         }
253     }
254 
255     public Provider getProvider() {
256         return SunNativeProvider.INSTANCE;
257     }
258 
259     public byte[] initSecContext(InputStream is, int mechTokenLen)
260         throws GSSException {
261         byte[] outToken = null;
262         if ((!isEstablished) && (isInitiator)) {
263             byte[] inToken = null;
264             // Ignore the specified input stream on the first call
265             if (pContext != 0) {
266                 inToken = retrieveToken(is, mechTokenLen);
267                 SunNativeProvider.debug("initSecContext=> inToken len=" +
268                     inToken.length);
269             }
270 
271             if (!getCredDelegState()) skipDelegPermCheck = true;
272 
273             if (GSSUtil.isKerberosMech(cStub.getMech()) && !skipDelegPermCheck) {
274                 doDelegPermCheck();
275             }
276 
277             long pCred = (cred == null? 0 : cred.pCred);
278             outToken = cStub.initContext(pCred, targetName.pName,
279                                          cb, inToken, this);
280             SunNativeProvider.debug("initSecContext=> outToken len=" +
281                 (outToken == null ? 0 : outToken.length));
282 
283             // Only inspect the token when the permission check
284             // has not been performed
285             if (GSSUtil.isSpNegoMech(cStub.getMech()) && outToken != null) {
286                 // WORKAROUND for SEAM bug#6287358
287                 actualMech = getMechFromSpNegoToken(outToken, true);
288 
289                 if (GSSUtil.isKerberosMech(actualMech)) {
290                     if (!skipServicePermCheck) doServicePermCheck();
291                     if (!skipDelegPermCheck) doDelegPermCheck();
292                 }
293             }
294 
295             if (isEstablished) {
296                 if (srcName == null) {
297                     srcName = new GSSNameElement
298                         (cStub.getContextName(pContext, true), cStub);
299                 }
300                 if (cred == null) {
301                     cred = new GSSCredElement(srcName, lifetime,
302                                               GSSCredential.INITIATE_ONLY,
303                                               cStub);
304                 }
305             }
306         }
307         return outToken;
308     }
309 
310     public byte[] acceptSecContext(InputStream is, int mechTokenLen)
311         throws GSSException {
312         byte[] outToken = null;
313         if ((!isEstablished) && (!isInitiator)) {
314             byte[] inToken = retrieveToken(is, mechTokenLen);
315             SunNativeProvider.debug("acceptSecContext=> inToken len=" +
316                                     inToken.length);
317             long pCred = (cred == null? 0 : cred.pCred);
318             outToken = cStub.acceptContext(pCred, cb, inToken, this);
319             SunNativeProvider.debug("acceptSecContext=> outToken len=" +
320                                     (outToken == null? 0 : outToken.length));
321 
322             if (targetName == null) {
323                 targetName = new GSSNameElement
324                     (cStub.getContextName(pContext, false), cStub);
325                 // Replace the current default acceptor cred now that
326                 // the context acceptor name is available
327                 if (cred != null) cred.dispose();
328                 cred = new GSSCredElement(targetName, lifetime,
329                                           GSSCredential.ACCEPT_ONLY, cStub);
330             }
331 
332             // Only inspect token when the permission check has not
333             // been performed
334             if (GSSUtil.isSpNegoMech(cStub.getMech()) &&
335                 (outToken != null) && !skipServicePermCheck) {
336                 if (GSSUtil.isKerberosMech(getMechFromSpNegoToken
337                                            (outToken, false))) {
338                     doServicePermCheck();
339                 }
340             }
341         }
342         return outToken;
343     }
344 
345     public boolean isEstablished() {
346         return isEstablished;
347     }
348 
349     public void dispose() throws GSSException {
350         srcName = null;
351         targetName = null;
352         cred = null;
353         delegatedCred = null;
354         if (pContext != 0) {
355             pContext = cStub.deleteContext(pContext);
356             pContext = 0;
357         }
358     }
359 
360     public int getWrapSizeLimit(int qop, boolean confReq,
361                                 int maxTokenSize)
362         throws GSSException {
363         return cStub.wrapSizeLimit(pContext, (confReq? 1:0), qop,
364                                    maxTokenSize);
365     }
366 
367     public byte[] wrap(byte[] inBuf, int offset, int len,
368                        MessageProp msgProp) throws GSSException {
369         byte[] data = inBuf;
370         if ((offset != 0) || (len != inBuf.length)) {
371             data = new byte[len];
372             System.arraycopy(inBuf, offset, data, 0, len);
373         }
374         return cStub.wrap(pContext, data, msgProp);
375     }
376     public void wrap(byte inBuf[], int offset, int len,
377                      OutputStream os, MessageProp msgProp)
378         throws GSSException {
379         try {
380         byte[] result = wrap(inBuf, offset, len, msgProp);
381         os.write(result);
382         } catch (IOException ioe) {
383             throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
384         }
385     }
386     public int wrap(byte[] inBuf, int inOffset, int len, byte[] outBuf,
387                     int outOffset, MessageProp msgProp)
388         throws GSSException {
389         byte[] result = wrap(inBuf, inOffset, len, msgProp);
390         System.arraycopy(result, 0, outBuf, outOffset, result.length);
391         return result.length;
392     }
393     public void wrap(InputStream inStream, OutputStream outStream,
394                      MessageProp msgProp) throws GSSException {
395         try {
396             byte[] data = new byte[inStream.available()];
397             int length = inStream.read(data);
398             byte[] token = wrap(data, 0, length, msgProp);
399             outStream.write(token);
400         } catch (IOException ioe) {
401             throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
402         }
403     }
404 
405     public byte[] unwrap(byte[] inBuf, int offset, int len,
406                          MessageProp msgProp)
407         throws GSSException {
408         if ((offset != 0) || (len != inBuf.length)) {
409             byte[] temp = new byte[len];
410             System.arraycopy(inBuf, offset, temp, 0, len);
411             return cStub.unwrap(pContext, temp, msgProp);
412         } else {
413             return cStub.unwrap(pContext, inBuf, msgProp);
414         }
415     }
416     public int unwrap(byte[] inBuf, int inOffset, int len,
417                       byte[] outBuf, int outOffset,
418                       MessageProp msgProp) throws GSSException {
419         byte[] result = null;
420         if ((inOffset != 0) || (len != inBuf.length)) {
421             byte[] temp = new byte[len];
422             System.arraycopy(inBuf, inOffset, temp, 0, len);
423             result = cStub.unwrap(pContext, temp, msgProp);
424         } else {
425             result = cStub.unwrap(pContext, inBuf, msgProp);
426         }
427         System.arraycopy(result, 0, outBuf, outOffset, result.length);
428         return result.length;
429     }
430     public void unwrap(InputStream inStream, OutputStream outStream,
431                        MessageProp msgProp) throws GSSException {
432         try {
433             byte[] wrapped = new byte[inStream.available()];
434             int wLength = inStream.read(wrapped);
435             byte[] data = unwrap(wrapped, 0, wLength, msgProp);
436             outStream.write(data);
437             outStream.flush();
438         } catch (IOException ioe) {
439             throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
440         }
441     }
442 
443     public int unwrap(InputStream inStream,
444                       byte[] outBuf, int outOffset,
445                       MessageProp msgProp) throws GSSException {
446         byte[] wrapped = null;
447         int wLength = 0;
448         try {
449             wrapped = new byte[inStream.available()];
450             wLength = inStream.read(wrapped);
451             byte[] result = unwrap(wrapped, 0, wLength, msgProp);
452         } catch (IOException ioe) {
453             throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
454         }
455         byte[] result = unwrap(wrapped, 0, wLength, msgProp);
456         System.arraycopy(result, 0, outBuf, outOffset, result.length);
457         return result.length;
458     }
459 
460     public byte[] getMIC(byte[] in, int offset, int len,
461                          MessageProp msgProp) throws GSSException {
462         int qop = (msgProp == null? 0:msgProp.getQOP());
463         byte[] inMsg = in;
464         if ((offset != 0) || (len != in.length)) {
465             inMsg = new byte[len];
466             System.arraycopy(in, offset, inMsg, 0, len);
467         }
468         return cStub.getMic(pContext, qop, inMsg);
469     }
470 
471     public void getMIC(InputStream inStream, OutputStream outStream,
472                        MessageProp msgProp) throws GSSException {
473         try {
474             int length = 0;
475             byte[] msg = new byte[inStream.available()];
476             length = inStream.read(msg);
477 
478             byte[] msgToken = getMIC(msg, 0, length, msgProp);
479             if ((msgToken != null) && msgToken.length != 0) {
480                 outStream.write(msgToken);
481             }
482         } catch (IOException ioe) {
483             throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
484         }
485     }
486 
487     public void verifyMIC(byte[] inToken, int tOffset, int tLen,
488                           byte[] inMsg, int mOffset, int mLen,
489                           MessageProp msgProp) throws GSSException {
490         byte[] token = inToken;
491         byte[] msg = inMsg;
492         if ((tOffset != 0) || (tLen != inToken.length)) {
493             token = new byte[tLen];
494             System.arraycopy(inToken, tOffset, token, 0, tLen);
495         }
496         if ((mOffset != 0) || (mLen != inMsg.length)) {
497             msg = new byte[mLen];
498             System.arraycopy(inMsg, mOffset, msg, 0, mLen);
499         }
500         cStub.verifyMic(pContext, token, msg, msgProp);
501     }
502 
503     public void verifyMIC(InputStream tokStream, InputStream msgStream,
504                           MessageProp msgProp) throws GSSException {
505         try {
506             byte[] msg = new byte[msgStream.available()];
507             int mLength = msgStream.read(msg);
508             byte[] tok = new byte[tokStream.available()];
509             int tLength = tokStream.read(tok);
510             verifyMIC(tok, 0, tLength, msg, 0, mLength, msgProp);
511         } catch (IOException ioe) {
512             throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
513         }
514     }
515 
516     public byte[] export() throws GSSException {
517         byte[] result = cStub.exportContext(pContext);
518         pContext = 0;
519         return result;
520     }
521 
522     private void changeFlags(int flagMask, boolean isEnable) {
523         if (isInitiator && pContext == 0) {
524             if (isEnable) {
525                 flags |= flagMask;
526             } else {
527                 flags &= ~flagMask;
528             }
529         }
530     }
531     public void requestMutualAuth(boolean state) throws GSSException {
532         changeFlags(GSS_C_MUTUAL_FLAG, state);
533     }
534     public void requestReplayDet(boolean state) throws GSSException {
535         changeFlags(GSS_C_REPLAY_FLAG, state);
536     }
537     public void requestSequenceDet(boolean state) throws GSSException {
538         changeFlags(GSS_C_SEQUENCE_FLAG, state);
539     }
540     public void requestCredDeleg(boolean state) throws GSSException {
541         changeFlags(GSS_C_DELEG_FLAG, state);
542     }
543     public void requestAnonymity(boolean state) throws GSSException {
544         changeFlags(GSS_C_ANON_FLAG, state);
545     }
546     public void requestConf(boolean state) throws GSSException {
547         changeFlags(GSS_C_CONF_FLAG, state);
548     }
549     public void requestInteg(boolean state) throws GSSException {
550         changeFlags(GSS_C_INTEG_FLAG, state);
551     }
552     public void requestDelegPolicy(boolean state) throws GSSException {
553         // Not supported, ignore
554     }
555     public void requestLifetime(int lifetime) throws GSSException {
556         if (isInitiator && pContext == 0) {
557             this.lifetime = lifetime;
558         }
559     }
560     public void setChannelBinding(ChannelBinding cb) throws GSSException {
561         if (pContext == 0) {
562             this.cb = cb;
563         }
564     }
565 
566     private boolean checkFlags(int flagMask) {
567         return ((flags & flagMask) != 0);
568     }
569     public boolean getCredDelegState() {
570         return checkFlags(GSS_C_DELEG_FLAG);
571     }
572     public boolean getMutualAuthState() {
573         return checkFlags(GSS_C_MUTUAL_FLAG);
574     }
575     public boolean getReplayDetState() {
576         return checkFlags(GSS_C_REPLAY_FLAG);
577     }
578     public boolean getSequenceDetState() {
579         return checkFlags(GSS_C_SEQUENCE_FLAG);
580     }
581     public boolean getAnonymityState() {
582         return checkFlags(GSS_C_ANON_FLAG);
583     }
584     public boolean isTransferable() throws GSSException {
585         return checkFlags(GSS_C_TRANS_FLAG);
586     }
587     public boolean isProtReady() {
588         return checkFlags(GSS_C_PROT_READY_FLAG);
589     }
590     public boolean getConfState() {
591         return checkFlags(GSS_C_CONF_FLAG);
592     }
593     public boolean getIntegState() {
594         return checkFlags(GSS_C_INTEG_FLAG);
595     }
596     public boolean getDelegPolicyState() {
597         return false;
598     }
599     public int getLifetime() {
600         return cStub.getContextTime(pContext);
601     }
602     public GSSNameSpi getSrcName() throws GSSException {
603         return srcName;
604     }
605     public GSSNameSpi getTargName() throws GSSException {
606         return targetName;
607     }
608     public Oid getMech() throws GSSException {
609         if (isEstablished && actualMech != null) {
610             return actualMech;
611         } else {
612             return cStub.getMech();
613         }
614     }
615     public GSSCredentialSpi getDelegCred() throws GSSException {
616         return delegatedCred;
617     }
618     public boolean isInitiator() {
619         return isInitiator;
620     }
621 
622     protected void finalize() throws Throwable {
623         dispose();
624     }
625 
626     public Object inquireSecContext(InquireType type)
627             throws GSSException {
628         throw new GSSException(GSSException.UNAVAILABLE, -1,
629                 "Inquire type not supported.");
630     }
631 }