KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > security > auth > kerberos > ServicePermission


1 /*
2  * @(#)ServicePermission.java 1.12 04/03/29
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.security.auth.kerberos;
9
10 import java.util.*;
11 import java.security.Permission JavaDoc;
12 import java.security.PermissionCollection JavaDoc;
13 import java.io.ObjectStreamField JavaDoc;
14 import java.io.ObjectOutputStream JavaDoc;
15 import java.io.ObjectInputStream JavaDoc;
16 import java.io.IOException JavaDoc;
17
18 /**
19  * This class is used to protect Kerberos services and the
20  * credentials necessary to access those services. There is a one to
21  * one mapping of a service principal and the credentials necessary
22  * to access the service. Therefore granting access to a service
23  * principal implicitly grants access to the credential necessary to
24  * establish a security context with the service principal. This
25  * applies regardless of whether the credentials are in a cache
26  * or acquired via an exchange with the KDC. The credential can
27  * be either a ticket granting ticket, a service ticket or a secret
28  * key from a key table.
29  * <p>
30  * A ServicePermission contains a service principal name and
31  * a list of actions which specify the context the credential can be
32  * used within.
33  * <p>
34  * The service principal name is the canonical name of the
35  * <code>KereberosPrincipal</code> supplying the service, that is
36  * the KerberosPrincipal represents a Kerberos service
37  * principal. This name is treated in a case sensitive manner.
38  * An asterisk may appear by itself, to signify any service principal.
39  * <p>
40  * Granting this permission implies that the caller can use a cached
41  * credential (TGT, service ticket or secret key) within the context
42  * designated by the action. In the case of the TGT, granting this
43  * permission also implies that the TGT can be obtained by an
44  * Authentication Service exchange.
45  * <p>
46  * The possible actions are:
47  * <p>
48  * <pre>
49  * initiate - allow the caller to use the credential to
50  * initiate a security context with a service
51  * principal.
52  *
53  * accept - allow the caller to use the credential to
54  * accept security context as a particular
55  * principal.
56  * </pre>
57  *
58  * For example, to specify the permission to access to the TGT to
59  * initiate a security context the permission is constructed as follows:
60  * <p>
61  * <pre>
62  * ServicePermission("krbtgt/EXAMPLE.COM@EXAMPLE.COM", "initiate");
63  * </pre>
64  * <p>
65  * To obtain a service ticket to initiate a context with the "host"
66  * service the permission is constructed as follows:
67  * <pre>
68  * ServicePermission("host/foo.example.com@EXAMPLE.COM", "initiate");
69  * </pre>
70  * <p>
71  * For a Kerberized server the action is "accept". For example, the permission
72  * necessary to access and use the secret key of the Kerberized "host"
73  * service (telnet and the likes) would be constructed as follows:
74  * <p>
75  * <pre>
76  * ServicePermission("host/foo.example.com@EXAMPLE.COM", "accept");
77  * </pre>
78  *
79  * @since JDK1.4
80  */

81
82 public final class ServicePermission extends Permission JavaDoc
83     implements java.io.Serializable JavaDoc {
84
85     private static final long serialVersionUID = -1227585031618624935L;
86
87     /**
88      * Initiate a security context to the specified service
89      */

90     private final static int INITIATE = 0x1;
91
92     /**
93      * Accept a security context
94      */

95     private final static int ACCEPT = 0x2;
96
97     /**
98      * All actions
99      */

100     private final static int ALL = INITIATE|ACCEPT;
101
102     /**
103      * No actions.
104      */

105     private final static int NONE = 0x0;
106
107     // the actions mask
108
private transient int mask;
109
110     /**
111      * the actions string.
112      *
113      * @serial
114      */

115
116     private String JavaDoc actions; // Left null as long as possible, then
117
// created and re-used in the getAction function.
118

119     /**
120      * Create a new <code>ServicePermission</code>
121      * with the specified <code>servicePrincipal</code>
122      * and <code>action</code>.
123      *
124      * @param servicePrincipal the name of the service principal.
125      * An asterisk may appear by itself, to signify any service principal.
126      * <p>
127      * @param action the action string
128      */

129     public ServicePermission(String JavaDoc servicePrincipal, String JavaDoc action) {
130     super(servicePrincipal);
131     init(servicePrincipal, getMask(action));
132     }
133
134
135     /**
136      * Initialize the ServicePermission object.
137      */

138     private void init(String JavaDoc servicePrincipal, int mask) {
139
140     if (servicePrincipal == null)
141         throw new NullPointerException JavaDoc("service principal can't be null");
142
143     if ((mask & ALL) != mask)
144         throw new IllegalArgumentException JavaDoc("invalid actions mask");
145
146     this.mask = mask;
147     }
148
149
150     /**
151      * Checks if this Kerberos service permission object "implies" the
152      * specified permission.
153      * <P>
154      * If none of the above are true, <code>implies</code> returns false.
155      * @param p the permission to check against.
156      *
157      * @return true if the specified permission is implied by this object,
158      * false if not.
159      */

160     public boolean implies(Permission JavaDoc p) {
161     if (!(p instanceof ServicePermission JavaDoc))
162         return false;
163
164     ServicePermission JavaDoc that = (ServicePermission JavaDoc) p;
165
166     return ((this.mask & that.mask) == that.mask) &&
167         impliesIgnoreMask(that);
168     }
169     
170     
171     boolean impliesIgnoreMask(ServicePermission JavaDoc p) {
172     return ((this.getName().equals("*")) ||
173         this.getName().equals(p.getName()));
174     }
175
176     /**
177      * Checks two ServicePermission objects for equality.
178      * <P>
179      * @param obj the object to test for equality with this object.
180      *
181      * @return true if <i>obj</i> is a ServicePermission, and has the
182      * same service principal, and actions as this
183      * ServicePermission object.
184      */

185     public boolean equals(Object JavaDoc obj) {
186     if (obj == this)
187         return true;
188
189     if (! (obj instanceof ServicePermission JavaDoc))
190         return false;
191
192     ServicePermission JavaDoc that = (ServicePermission JavaDoc) obj;
193     return ((this.mask & that.mask) == that.mask) &&
194         this.getName().equals(that.getName());
195         
196
197     }
198
199     /**
200      * Returns the hash code value for this object.
201      *
202      * @return a hash code value for this object.
203      */

204
205     public int hashCode() {
206     return (getName().hashCode() ^ mask);
207     }
208     
209
210     /**
211      * Returns the "canonical string representation" of the actions in the
212      * specified mask.
213      * Always returns present actions in the following order:
214      * initiate, accept.
215      *
216      * @param mask a specific integer action mask to translate into a string
217      * @return the canonical string representation of the actions
218      */

219     private static String JavaDoc getActions(int mask)
220     {
221     StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
222         boolean comma = false;
223
224     if ((mask & INITIATE) == INITIATE) {
225         if (comma) sb.append(',');
226             else comma = true;
227         sb.append("initiate");
228     }
229
230     if ((mask & ACCEPT) == ACCEPT) {
231         if (comma) sb.append(',');
232             else comma = true;
233         sb.append("accept");
234     }
235
236     return sb.toString();
237     }
238
239     /**
240      * Returns the canonical string representation of the actions.
241      * Always returns present actions in the following order:
242      * initiate, accept.
243      */

244     
245     public String JavaDoc getActions() {
246     if (actions == null)
247         actions = getActions(this.mask);
248
249     return actions;
250     }
251
252     
253     /**
254      * Returns a PermissionCollection object for storing
255      * ServicePermission objects.
256      * <br>
257      * ServicePermission objects must be stored in a manner that
258      * allows them to be inserted into the collection in any order, but
259      * that also enables the PermissionCollection implies method to
260      * be implemented in an efficient (and consistent) manner.
261      *
262      * @return a new PermissionCollection object suitable for storing
263      * ServicePermissions.
264      */

265
266     public PermissionCollection JavaDoc newPermissionCollection() {
267     return new KrbServicePermissionCollection();
268     }
269
270     /**
271      * Return the current action mask.
272      *
273      * @return the actions mask.
274      */

275
276     int getMask() {
277     return mask;
278     }
279
280     /**
281      * Convert an action string to an integer actions mask.
282      *
283      * @param action the action string
284      * @return the action mask
285      */

286
287     private static int getMask(String JavaDoc action) {
288
289     if (action == null) {
290         throw new NullPointerException JavaDoc("action can't be null");
291     }
292
293     if (action.equals("")) {
294         throw new IllegalArgumentException JavaDoc("action can't be empty");
295     }
296
297     int mask = NONE;
298
299     if (action == null) {
300         return mask;
301     }
302
303     char[] a = action.toCharArray();
304
305     int i = a.length - 1;
306     if (i < 0)
307         return mask;
308
309     while (i != -1) {
310         char c;
311
312         // skip whitespace
313
while ((i!=-1) && ((c = a[i]) == ' ' ||
314                    c == '\r' ||
315                    c == '\n' ||
316                    c == '\f' ||
317                    c == '\t'))
318         i--;
319
320         // check for the known strings
321
int matchlen;
322
323         if (i >= 7 && (a[i-7] == 'i' || a[i-7] == 'I') &&
324               (a[i-6] == 'n' || a[i-6] == 'N') &&
325               (a[i-5] == 'i' || a[i-5] == 'I') &&
326               (a[i-4] == 't' || a[i-4] == 'T') &&
327               (a[i-3] == 'i' || a[i-3] == 'I') &&
328               (a[i-2] == 'a' || a[i-2] == 'A') &&
329               (a[i-1] == 't' || a[i-1] == 'T') &&
330               (a[i] == 'e' || a[i] == 'E'))
331         {
332         matchlen = 8;
333         mask |= INITIATE;
334
335         } else if (i >= 5 && (a[i-5] == 'a' || a[i-5] == 'A') &&
336                  (a[i-4] == 'c' || a[i-4] == 'C') &&
337                  (a[i-3] == 'c' || a[i-3] == 'C') &&
338                  (a[i-2] == 'e' || a[i-2] == 'E') &&
339                  (a[i-1] == 'p' || a[i-1] == 'P') &&
340                  (a[i] == 't' || a[i] == 'T'))
341         {
342         matchlen = 6;
343         mask |= ACCEPT;
344
345         } else {
346         // parse error
347
throw new IllegalArgumentException JavaDoc(
348             "invalid permission: " + action);
349         }
350
351         // make sure we didn't just match the tail of a word
352
// like "ackbarfaccept". Also, skip to the comma.
353
boolean seencomma = false;
354         while (i >= matchlen && !seencomma) {
355         switch(a[i-matchlen]) {
356         case ',':
357             seencomma = true;
358             /*FALLTHROUGH*/
359         case ' ': case '\r': case '\n':
360         case '\f': case '\t':
361             break;
362         default:
363             throw new IllegalArgumentException JavaDoc(
364                 "invalid permission: " + action);
365         }
366         i--;
367         }
368
369         // point i at the location of the comma minus one (or -1).
370
i -= matchlen;
371     }
372
373     return mask;
374     }
375
376
377     /**
378      * WriteObject is called to save the state of the ServicePermission
379      * to a stream. The actions are serialized, and the superclass
380      * takes care of the name.
381      */

382     private synchronized void writeObject(java.io.ObjectOutputStream JavaDoc s)
383         throws IOException JavaDoc
384     {
385     // Write out the actions. The superclass takes care of the name
386
// call getActions to make sure actions field is initialized
387
if (actions == null)
388         getActions();
389     s.defaultWriteObject();
390     }
391
392     /**
393      * readObject is called to restore the state of the
394      * ServicePermission from a stream.
395      */

396     private synchronized void readObject(java.io.ObjectInputStream JavaDoc s)
397          throws IOException JavaDoc, ClassNotFoundException JavaDoc
398     {
399     // Read in the action, then initialize the rest
400
s.defaultReadObject();
401     init(getName(),getMask(actions));
402     }
403
404
405     /*
406       public static void main(String args[]) throws Exception {
407       ServicePermission this_ =
408       new ServicePermission(args[0], "accept");
409       ServicePermission that_ =
410       new ServicePermission(args[1], "accept,initiate");
411       System.out.println("-----\n");
412       System.out.println("this.implies(that) = " + this_.implies(that_));
413       System.out.println("-----\n");
414       System.out.println("this = "+this_);
415       System.out.println("-----\n");
416       System.out.println("that = "+that_);
417       System.out.println("-----\n");
418
419       KrbServicePermissionCollection nps =
420       new KrbServicePermissionCollection();
421       nps.add(this_);
422       nps.add(new ServicePermission("nfs/example.com@EXAMPLE.COM",
423       "accept"));
424       nps.add(new ServicePermission("host/example.com@EXAMPLE.COM",
425       "initiate"));
426       System.out.println("nps.implies(that) = " + nps.implies(that_));
427       System.out.println("-----\n");
428
429       Enumeration e = nps.elements();
430     
431       while (e.hasMoreElements()) {
432       ServicePermission x =
433       (ServicePermission) e.nextElement();
434       System.out.println("nps.e = " + x);
435       }
436
437       }
438     */

439     
440 }
441
442
443 final class KrbServicePermissionCollection extends PermissionCollection JavaDoc
444     implements java.io.Serializable JavaDoc {
445
446     // Not serialized; see serialization section at end of class
447
private transient List perms;
448
449     public KrbServicePermissionCollection() {
450     perms = new ArrayList();
451     }
452     
453     /**
454      * Check and see if this collection of permissions implies the permissions
455      * expressed in "permission".
456      *
457      * @param p the Permission object to compare
458      *
459      * @return true if "permission" is a proper subset of a permission in
460      * the collection, false if not.
461      */

462
463     public boolean implies(Permission JavaDoc permission) {
464     if (! (permission instanceof ServicePermission JavaDoc))
465         return false;
466
467     ServicePermission JavaDoc np = (ServicePermission JavaDoc) permission;
468     int desired = np.getMask();
469     int effective = 0;
470     int needed = desired;
471
472     synchronized (this) {
473         int len = perms.size();
474     
475         // need to deal with the case where the needed permission has
476
// more than one action and the collection has individual permissions
477
// that sum up to the needed.
478

479         for (int i = 0; i < len; i++) {
480         ServicePermission JavaDoc x = (ServicePermission JavaDoc) perms.get(i);
481
482         //System.out.println(" trying "+x);
483
if (((needed & x.getMask()) != 0) && x.impliesIgnoreMask(np)) {
484             effective |= x.getMask();
485             if ((effective & desired) == desired)
486             return true;
487             needed = (desired ^ effective);
488         }
489         }
490     }
491     return false;
492     }
493
494     /**
495      * Adds a permission to the ServicePermissions. The key for
496      * the hash is the name.
497      *
498      * @param permission the Permission object to add.
499      *
500      * @exception IllegalArgumentException - if the permission is not a
501      * ServicePermission
502      *
503      * @exception SecurityException - if this PermissionCollection object
504      * has been marked readonly
505      */

506
507     public void add(Permission JavaDoc permission) {
508     if (! (permission instanceof ServicePermission JavaDoc))
509         throw new IllegalArgumentException JavaDoc("invalid permission: "+
510                            permission);
511     if (isReadOnly())
512         throw new SecurityException JavaDoc("attempt to add a Permission to a readonly PermissionCollection");
513
514     synchronized (this) {
515         perms.add(0, permission);
516     }
517     }
518
519     /**
520      * Returns an enumeration of all the ServicePermission objects
521      * in the container.
522      *
523      * @return an enumeration of all the ServicePermission objects.
524      */

525
526     public Enumeration elements() {
527         // Convert Iterator into Enumeration
528
synchronized (this) {
529         return Collections.enumeration(perms);
530     }
531     }
532
533     private static final long serialVersionUID = -4118834211490102011L;
534
535     // Need to maintain serialization interoperability with earlier releases,
536
// which had the serializable field:
537
// private Vector permissions;
538

539     /**
540      * @serialField permissions java.util.Vector
541      * A list of ServicePermission objects.
542      */

543     private static final ObjectStreamField JavaDoc[] serialPersistentFields = {
544         new ObjectStreamField JavaDoc("permissions", Vector.class),
545     };
546
547     /**
548      * @serialData "permissions" field (a Vector containing the ServicePermissions).
549      */

550     /*
551      * Writes the contents of the perms field out as a Vector for
552      * serialization compatibility with earlier releases.
553      */

554     private void writeObject(ObjectOutputStream JavaDoc out) throws IOException JavaDoc {
555     // Don't call out.defaultWriteObject()
556

557     // Write out Vector
558
Vector permissions = new Vector(perms.size());
559
560     synchronized (this) {
561         permissions.addAll(perms);
562     }
563
564         ObjectOutputStream.PutField JavaDoc pfields = out.putFields();
565         pfields.put("permissions", permissions);
566         out.writeFields();
567     }
568
569     /*
570      * Reads in a Vector of ServicePermissions and saves them in the perms field.
571      */

572     private void readObject(ObjectInputStream JavaDoc in) throws IOException JavaDoc,
573     ClassNotFoundException JavaDoc {
574     // Don't call defaultReadObject()
575

576     // Read in serialized fields
577
ObjectInputStream.GetField JavaDoc gfields = in.readFields();
578
579     // Get the one we want
580
Vector permissions = (Vector)gfields.get("permissions", null);
581     perms = new ArrayList(permissions.size());
582     perms.addAll(permissions);
583     }
584 }
585
Popular Tags