001: /*
002: * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.security.jgss.krb5;
027:
028: import org.ietf.jgss.*;
029: import sun.security.jgss.spi.*;
030: import javax.security.auth.kerberos.*;
031: import sun.security.krb5.PrincipalName;
032: import sun.security.krb5.KrbException;
033: import sun.security.krb5.ServiceName;
034: import java.io.UnsupportedEncodingException;
035: import java.net.InetAddress;
036: import java.net.UnknownHostException;
037: import java.security.Provider;
038:
039: /**
040: * Implements the GSSNameSpi for the krb5 mechanism.
041: *
042: * @author Mayank Upadhyay
043: * @version 1.23, 05/05/07
044: */
045: public class Krb5NameElement implements GSSNameSpi {
046:
047: private PrincipalName krb5PrincipalName;
048:
049: private String gssNameStr = null;
050: private Oid gssNameType = null;
051:
052: // XXX Move this concept into PrincipalName's asn1Encode() sometime
053: private static String CHAR_ENCODING = "UTF-8";
054:
055: private Krb5NameElement(PrincipalName principalName,
056: String gssNameStr, Oid gssNameType) {
057: this .krb5PrincipalName = principalName;
058: this .gssNameStr = gssNameStr;
059: this .gssNameType = gssNameType;
060: }
061:
062: /**
063: * Instantiates a new Krb5NameElement object. Internally it stores the
064: * information provided by the input parameters so that they may later
065: * be used for output when a printable representaion of this name is
066: * needed in GSS-API format rather than in Kerberos format.
067: *
068: */
069: static Krb5NameElement getInstance(String gssNameStr,
070: Oid gssNameType) throws GSSException {
071:
072: /*
073: * A null gssNameType implies that the mechanism default
074: * Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL be used.
075: */
076: if (gssNameType == null)
077: gssNameType = Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL;
078: else if (!gssNameType.equals(GSSName.NT_USER_NAME)
079: && !gssNameType.equals(GSSName.NT_HOSTBASED_SERVICE)
080: && !gssNameType
081: .equals(Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL)
082: && !gssNameType.equals(GSSName.NT_EXPORT_NAME))
083: throw new GSSException(GSSException.BAD_NAMETYPE, -1,
084: gssNameType.toString()
085: + " is an unsupported nametype");
086:
087: PrincipalName principalName;
088: try {
089:
090: if (gssNameType.equals(GSSName.NT_EXPORT_NAME)
091: || gssNameType
092: .equals(Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL)) {
093: principalName = new PrincipalName(gssNameStr,
094: PrincipalName.KRB_NT_PRINCIPAL);
095: } else {
096:
097: String[] components = getComponents(gssNameStr);
098:
099: /*
100: * We have forms of GSS name strings that can come in:
101: *
102: * 1. names of the form "foo" with just one
103: * component. (This might include a "@" but only in escaped
104: * form like "\@")
105: * 2. names of the form "foo@bar" with two components
106: *
107: * The nametypes that are accepted are NT_USER_NAME, and
108: * NT_HOSTBASED_SERVICE.
109: */
110:
111: if (gssNameType.equals(GSSName.NT_USER_NAME))
112: principalName = new PrincipalName(gssNameStr,
113: PrincipalName.KRB_NT_PRINCIPAL);
114: else {
115: String hostName = null;
116: String service = components[0];
117: if (components.length >= 2)
118: hostName = components[1];
119:
120: String principal = getHostBasedInstance(service,
121: hostName);
122: principalName = new ServiceName(principal,
123: PrincipalName.KRB_NT_SRV_HST);
124: }
125: }
126:
127: } catch (KrbException e) {
128: throw new GSSException(GSSException.BAD_NAME, -1, e
129: .getMessage());
130: }
131:
132: return new Krb5NameElement(principalName, gssNameStr,
133: gssNameType);
134: }
135:
136: static Krb5NameElement getInstance(PrincipalName principalName) {
137: return new Krb5NameElement(principalName, principalName
138: .getName(), Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
139: }
140:
141: private static String[] getComponents(String gssNameStr)
142: throws GSSException {
143:
144: String[] retVal;
145:
146: // XXX Perhaps provide this parsing code in PrincipalName
147:
148: // Look for @ as in service@host
149: // Assumes host name will not have an escaped '@'
150: int separatorPos = gssNameStr.lastIndexOf('@', gssNameStr
151: .length());
152:
153: // Not really a separator if it is escaped. Then this is just part
154: // of the principal name or service name
155: if ((separatorPos > 0)
156: && (gssNameStr.charAt(separatorPos - 1) == '\\')) {
157: // Is the `\` character escaped itself?
158: if ((separatorPos - 2 < 0)
159: || (gssNameStr.charAt(separatorPos - 2) != '\\'))
160: separatorPos = -1;
161: }
162:
163: if (separatorPos > 0) {
164: String serviceName = gssNameStr.substring(0, separatorPos);
165: String hostName = gssNameStr.substring(separatorPos + 1);
166: retVal = new String[] { serviceName, hostName };
167: } else {
168: retVal = new String[] { gssNameStr };
169: }
170:
171: return retVal;
172:
173: }
174:
175: private static String getHostBasedInstance(String serviceName,
176: String hostName) throws GSSException {
177: StringBuffer temp = new StringBuffer(serviceName);
178:
179: try {
180: // A lack of "@" defaults to the service being on the local
181: // host as per RFC 2743
182: // XXX Move this part into JGSS framework
183: if (hostName == null)
184: hostName = InetAddress.getLocalHost().getHostName();
185:
186: } catch (UnknownHostException e) {
187: // use hostname as it is
188: }
189: hostName = hostName.toLowerCase();
190:
191: temp = temp.append('/').append(hostName);
192: return temp.toString();
193: }
194:
195: public final PrincipalName getKrb5PrincipalName() {
196: return krb5PrincipalName;
197: }
198:
199: /**
200: * Equal method for the GSSNameSpi objects.
201: * If either name denotes an anonymous principal, the call should
202: * return false.
203: *
204: * @param name to be compared with
205: * @returns true if they both refer to the same entity, else false
206: * @exception GSSException with major codes of BAD_NAMETYPE,
207: * BAD_NAME, FAILURE
208: */
209: public boolean equals(GSSNameSpi other) throws GSSException {
210:
211: if (other == this )
212: return true;
213:
214: if (other instanceof Krb5NameElement) {
215: Krb5NameElement that = (Krb5NameElement) other;
216: return (this .krb5PrincipalName.getName()
217: .equals(that.krb5PrincipalName.getName()));
218: }
219: return false;
220: }
221:
222: /**
223: * Compares this <code>GSSNameSpi</code> object to another Object
224: * that might be a <code>GSSNameSpi</code>. The behaviour is exactly
225: * the same as in {@link #equals(GSSNameSpi) equals} except that
226: * no GSSException is thrown; instead, false will be returned in the
227: * situation where an error occurs.
228: *
229: * @param another the object to be compared to
230: * @returns true if they both refer to the same entity, else false
231: * @see #equals(GSSNameSpi)
232: */
233: public boolean equals(Object another) {
234: if (this == another) {
235: return true;
236: }
237:
238: try {
239: if (another instanceof Krb5NameElement)
240: return equals((Krb5NameElement) another);
241: } catch (GSSException e) {
242: // ignore exception
243: }
244: return false;
245: }
246:
247: /**
248: * Returns a hashcode value for this GSSNameSpi.
249: *
250: * @return a hashCode value
251: */
252: public int hashCode() {
253: return 37 * 17 + krb5PrincipalName.getName().hashCode();
254: }
255:
256: /**
257: * Returns the principal name in the form user@REALM or
258: * host/service@REALM but with the following contraints that are
259: * imposed by RFC 1964:
260: * <pre>
261: * (1) all occurrences of the characters `@`, `/`, and `\` within
262: * principal components or realm names shall be quoted with an
263: * immediately-preceding `\`.
264: *
265: * (2) all occurrences of the null, backspace, tab, or newline
266: * characters within principal components or realm names will be
267: * represented, respectively, with `\0`, `\b`, `\t`, or `\n`.
268: *
269: * (3) the `\` quoting character shall not be emitted within an
270: * exported name except to accomodate cases (1) and (2).
271: * </pre>
272: */
273: public byte[] export() throws GSSException {
274: // XXX Apply the above constraints.
275: byte[] retVal = null;
276: try {
277: retVal = krb5PrincipalName.getName()
278: .getBytes(CHAR_ENCODING);
279: } catch (UnsupportedEncodingException e) {
280: // Can't happen
281: }
282: return retVal;
283: }
284:
285: /**
286: * Get the mechanism type that this NameElement corresponds to.
287: *
288: * @return the Oid of the mechanism type
289: */
290: public Oid getMechanism() {
291: return (Krb5MechFactory.GSS_KRB5_MECH_OID);
292: }
293:
294: /**
295: * Returns a string representation for this name. The printed
296: * name type can be obtained by calling getStringNameType().
297: *
298: * @return string form of this name
299: * @see #getStringNameType()
300: * @overrides Object#toString
301: */
302: public String toString() {
303: return (gssNameStr);
304: // For testing: return (super.toString());
305: }
306:
307: /**
308: * Returns the name type oid.
309: */
310: public Oid getGSSNameType() {
311: return (gssNameType);
312: }
313:
314: /**
315: * Returns the oid describing the format of the printable name.
316: *
317: * @return the Oid for the format of the printed name
318: */
319: public Oid getStringNameType() {
320: // XXX For NT_EXPORT_NAME return a different name type. Infact,
321: // don't even store NT_EXPORT_NAME in the cons.
322: return (gssNameType);
323: }
324:
325: /**
326: * Indicates if this name object represents an Anonymous name.
327: */
328: public boolean isAnonymousName() {
329: return (gssNameType.equals(GSSName.NT_ANONYMOUS));
330: }
331:
332: public Provider getProvider() {
333: return Krb5MechFactory.PROVIDER;
334: }
335:
336: }
|