View | Details | Raw Unified | Return to bug 52500
Collapse All | Expand All

(-)org/apache/catalina/realm/X509SubjectDnRetriever.java (+204 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
19
package org.apache.catalina.realm;
20
21
import java.security.cert.X509Certificate;
22
import java.util.Arrays;
23
import java.util.List;
24
25
import javax.naming.InvalidNameException;
26
import javax.naming.ldap.LdapName;
27
import javax.naming.ldap.Rdn;
28
29
import org.apache.juli.logging.Log;
30
import org.apache.juli.logging.LogFactory;
31
32
33
/**
34
 * Implementation of <b>X509UserNameRetriever</b> that takes a user name from the Subject Field of the X509Certificate.
35
 * The user name is the unique part of information from the client certificate that used to identify the identity of the user. 
36
 * The Subject field (also called Subject Distinguish Name or SubjectDN) identifies the entity associated with the public key.
37
 * The Subject field contains the following relevant attributes (it can also contain other attributes).
38
 * 
39
 * Subject Attribute	Subject Attribute Description		Example
40
 * CN					Common Name							CN=Bob BobFamily
41
 * emailAddress			Email Address						emailAddress=bob@example.com
42
 * C					Country Name						C=US
43
 * ST					State or Province Name				ST=NY
44
 * L					Locality Name						L=New York
45
 * O					Organization Name					O=Work Organization
46
 * OU					Organizational Unit Name			OU=Managers
47
 * 
48
 * To retrieve the user name from the subject, you can use the entire SubjectDN field or the SubjectDN attribute.
49
 * To retrieve the user name from entire SubjectDN field use the default constructor
50
 * To retrieve the user name from the SubjectDN attribute, please provide the retrieve attribute name
51
 * The the retrieve attribute name is a code letter based on a legend defined in the certificate itself.
52
 * 
53
 * For example, the Email attribute is used to hold the User Name.
54
 * Please provide "e" or "emailAddress" for the constructor.
55
 * 
56
 * For example, the Common Name attribute is used to hold the User Name.
57
 * Please provide "CN" for the constructor.
58
 * 
59
 *
60
 */
61
public class X509SubjectDnRetriever implements X509UserNameRetriever {
62
	/**
63
	 * Logger for this class
64
	 */
65
	protected final Log log = LogFactory.getLog(getClass());	
66
	 
67
	private static final String EMAIL_SUBJECT_ATTR = "emailAddress";
68
69
	
70
	/**
71
	 * The value is based on a legend defined in the certificate itself.
72
	 * It tested with the following browsers: IE, FF and Chrome
73
	 * For new browsers may be will required to add a new legend value to the list.
74
	 */
75
	private static final List<String> EmailOptions = Arrays.asList(EMAIL_SUBJECT_ATTR.toLowerCase(), "e") ;
76
77
	
78
	private String subjectDnAttribute = null;
79
	private String subjectDnAttributeConfiguration = null;
80
	
81
    protected X509SubjectDnRetriever() {
82
        setSubjectDnAttribute(null);
83
    }
84
85
    protected X509SubjectDnRetriever(String retrieveAttr) {
86
        setSubjectDnAttribute(retrieveAttr);
87
    }
88
    
89
    /**
90
     * Set the configuration for X509UserNameRetriever. The X509UserNameRetriever uses the configuration 
91
     * to retrieve a user name from X509Certificate. 
92
     * 
93
     * @param x509UserNameRetrieverConfiguration
94
     */
95
    public void setX509UserNameRetrieverConfiguration(String x509UserNameRetrieverConfiguration) { 
96
    	setSubjectDnAttribute(x509UserNameRetrieverConfiguration);
97
		
98
	}
99
    /* (non-Javadoc)
100
     * @see org.apache.catalina.realm.X509UserNameRetriever#getUserName(java.security.cert.X509Certificate)
101
     */
102
    public String getUserName(X509Certificate clientCert) {
103
		if (log.isDebugEnabled()) {
104
			log.debug("getUserName(X509Certificate) - start");
105
		}
106
		String subject = getSubjectDN(clientCert);
107
		String userName = null;
108
		
109
		if (subject != null) {
110
			if (log.isDebugEnabled()) {
111
				log.debug("Subject is [" + subject + "].");
112
			}
113
			if (subjectDnAttribute == null) {
114
				if (log.isDebugEnabled()) {
115
					log.debug("subjectDnAttribute is null, so return the whole subject.");
116
				}
117
				userName = subject;
118
			} else {
119
				boolean foundUserName = false;
120
				try {					
121
					LdapName ldapName = new LdapName(subject);
122
					List<Rdn> list = ldapName.getRdns();
123
					if (list != null) {
124
						for (Rdn rdn : list) {
125
							String type = rdn.getType();
126
							if (subjectDnAttribute.equalsIgnoreCase(type.toString()))  {
127
								Object value = rdn.getValue();
128
								if (value instanceof String) {
129
									userName = (String) value;
130
									foundUserName = true;									
131
									if (log.isDebugEnabled()) {
132
										log.debug("Success to retreive userName [" + userName + "].");
133
									}
134
									break;
135
								}		
136
							}
137
						}
138
					}					
139
				} catch (InvalidNameException e) {					
140
					log.info("subject [" + subject + "] is not valid name : [" + e.getMessage() + "].");
141
				}
142
				if (!foundUserName) {
143
					log.info("subject [" + subject + "] does not contain the required attribute [" + subjectDnAttributeConfiguration + "]. Return the whole subject.");
144
					userName = subject;
145
				}					
146
			}
147
			
148
		}
149
			
150
		if (log.isDebugEnabled()) {
151
			log.debug("getUserName(X509Certificate) - end; Ret is [" + userName + "].");
152
		}
153
        return userName;
154
    }
155
156
    private void setSubjectDnAttribute(String subjectDnAttributeConfiguration) {
157
		this.subjectDnAttributeConfiguration = subjectDnAttributeConfiguration;
158
		subjectDnAttribute = mapSubjectDnAttribute(subjectDnAttributeConfiguration);
159
		if (log.isDebugEnabled()) {
160
			log.debug("setSubjectDnAttribute(String) - end; subjectDnAttribute [" +  subjectDnAttribute + "]; subjectDnAttributeConfiguration [" +  subjectDnAttributeConfiguration + "]");
161
		}
162
    }
163
    
164
    
165
166
	private String mapSubjectDnAttribute(String subjectDnAttributeConfiguration) {
167
		String ret = null;
168
		if (subjectDnAttributeConfiguration != null) {
169
			if (EmailOptions.contains(subjectDnAttributeConfiguration.toLowerCase()))  {
170
				ret = EMAIL_SUBJECT_ATTR;				
171
			} else {
172
				ret = subjectDnAttributeConfiguration;
173
			}
174
		}
175
		return ret;
176
	}
177
178
	/**
179
	 * @param clientCert
180
	 * @return the whole SubjectSN of the X509Certificate
181
	 */
182
	protected String getSubjectDN(X509Certificate clientCert) {
183
		String subject = null;
184
		if (clientCert != null) {
185
			if ((clientCert.getSubjectDN()!= null) 
186
					&& (clientCert.getSubjectDN().getName() != null)) {
187
					subject = clientCert.getSubjectDN().getName();
188
			}  else {
189
				if (log.isDebugEnabled()) {
190
					log.debug("Can not getSubjectDN, SubjectDN is null");
191
				}
192
			}
193
		} else {
194
			if (log.isDebugEnabled()) {
195
				log.debug("Can not getSubjectDN, clientCert is null");
196
			}
197
		}			
198
		if (log.isDebugEnabled()) {
199
			log.debug("getSubjectDN(X509Certificate) - end; Ret is [" + subject + "].");
200
		}
201
        return subject;
202
        
203
	}
204
}
(-)org/apache/catalina/realm/RealmBase.java (-1 / +127 lines)
Lines 150-155 Link Here
150
    protected boolean stripRealmForGss = true;
150
    protected boolean stripRealmForGss = true;
151
151
152
152
153
    /**
154
     * The <b>X509UserNameRetriever</b> is used to get the user name during the client certificate authentication
155
     */
156
    private X509UserNameRetriever x509UserNameRetriever = null;
157
158
    
159
	/**
160
	 * The value indicates which part of constitutes the username. 
161
     * The value is a code letter based on a legend defined in the certificate itself.
162
     */
163
	 
164
	private String x509UserNameRetrieverConfiguration = null;
165
    
166
	/**
167
     * @param className - the class that implements <b>X509UserNameRetriever</b>
168
     * If the value is provided a realm will create X509UserNameRetriever.
169
	 * 
170
	 */
171
	private String x509UserNameRetrieverClassName = null;
172
    
153
    // ------------------------------------------------------------- Properties
173
    // ------------------------------------------------------------- Properties
154
174
155
175
Lines 1034-1039 Link Here
1034
        if (container != null) {
1054
        if (container != null) {
1035
            this.containerLog = container.getLogger();
1055
            this.containerLog = container.getLogger();
1036
        }
1056
        }
1057
        // Create x509UserNameRetriever for the client certificate authentication.
1058
        createUserNameRetriever(x509UserNameRetrieverConfiguration, x509UserNameRetrieverClassName);
1037
    }
1059
    }
1038
1060
1039
    /**
1061
    /**
Lines 1191-1197 Link Here
1191
     * Return the Principal associated with the given certificate.
1213
     * Return the Principal associated with the given certificate.
1192
     */
1214
     */
1193
    protected Principal getPrincipal(X509Certificate usercert) {
1215
    protected Principal getPrincipal(X509Certificate usercert) {
1194
        return(getPrincipal(usercert.getSubjectDN().getName()));
1216
    	// get user name using x509UserNameRetriever
1217
		String userName = x509UserNameRetriever.getUserName(usercert);
1218
1219
		if (log.isDebugEnabled())
1220
            log.debug("Get principal for [" + userName + "]");		
1221
        return(getPrincipal(userName));
1195
    }
1222
    }
1196
1223
1197
1224
Lines 1390-1394 Link Here
1390
            return name;
1417
            return name;
1391
        }
1418
        }
1392
    }
1419
    }
1420
    
1421
    /**
1422
     * @param x509UserNameRetrieverConfiguration - the value is used to configure how to get the user name during the client certificate authentication. 
1423
     * 
1424
     * The user name is the unique part of information from the client certificate that used to identify the identity of the user. 
1425
     * The Subject field (also called Subject Distinguish Name or SubjectDN) identifies the entity associated with the public key.
1426
     * The Subject field contains the following relevant attributes (it can also contain other attributes).
1427
	 * 
1428
	 * Subject Attribute	Subject Attribute Description		Example
1429
	 * CN					Common Name							CN=Bob BobFamily
1430
	 * emailAddress			Email Address						emailAddress=bob@example.com
1431
	 * C					Country Name						C=US
1432
	 * ST					State or Province Name				ST=NY
1433
	 * L					Locality Name						L=New York
1434
	 * O					Organization Name					O=Work Organization
1435
	 * OU					Organizational Unit Name			OU=Managers
1436
	 * 
1437
	 * To retrieve the user name from the subject, you can use the entire SubjectDN field or the SubjectDN attribute.
1438
	 * To retrieve the user name from entire SubjectDN field leave the value empty.
1439
	 * To retrieve the user name from the SubjectDN attribute, please provide the retrieve attribute name.
1440
	 * The the retrieve attribute name is a code letter based on a legend defined in the certificate itself.
1441
	 * 
1442
	 * For example, the Email attribute is used to hold the User Name.
1443
	 * Please provide "e" or "emailAddress" for the constructor.
1444
	 * 
1445
	 * For example, the Common Name attribute is used to hold the User Name.
1446
	 * Please provide "CN" for the constructor.
1447
	 * 
1448
     */
1449
	public void setX509UserNameRetrieverConfiguration(String x509UserNameRetrieverConfiguration) {
1450
		this.x509UserNameRetrieverConfiguration = x509UserNameRetrieverConfiguration;
1451
	}
1393
1452
1453
    /**
1454
     * @param className - The Java class name that is used to override the default <b>X509UserNameRetriever.</b> 
1455
     * The X509UserNameRetriever is used to get the user name during the client certificate authentication.
1456
     * If the value is provided a realm will create X509UserNameRetriever from the provided class. 
1457
     * This class must implement the <code>org.apache.catalina.realm.X509UserNameRetriever</code> interface.
1458
     */
1459
	
1460
	public void setX509UserNameRetrieverClassName(String className) {
1461
		 x509UserNameRetrieverClassName = className;
1462
	}
1463
	
1464
	
1465
	private void createUserNameRetriever(String x509UserNameRetrieverConfiguration, String x509UserNameRetrieverClassName) {
1466
		
1467
		boolean createdFromClassName = createUserNameRetrieverFromClassName(x509UserNameRetrieverClassName);
1468
		if (createdFromClassName) {
1469
			if (x509UserNameRetriever instanceof X509SubjectDnRetriever) {
1470
				// set the configuration to x509UserNameRetriever
1471
				((X509SubjectDnRetriever) x509UserNameRetriever).setX509UserNameRetrieverConfiguration(x509UserNameRetrieverConfiguration);
1472
			}
1473
		} else {
1474
			if (x509UserNameRetrieverConfiguration == null) {
1475
				x509UserNameRetriever = new X509SubjectDnRetriever();
1476
			} else {
1477
				x509UserNameRetriever = new X509SubjectDnRetriever(x509UserNameRetrieverConfiguration);
1478
			}
1479
		}
1480
	}
1481
1482
	
1483
1484
	@SuppressWarnings("unchecked")
1485
	private boolean createUserNameRetrieverFromClassName(String x509UserNameRetrieverClassName) {
1486
1487
		boolean created = false;
1488
		if ((x509UserNameRetrieverClassName != null)
1489
				&& (x509UserNameRetrieverClassName != "")) {
1490
			Class<? extends X509UserNameRetriever> x509UserNameRetrieverClass = null;
1491
			try {
1492
				x509UserNameRetrieverClass = (Class<? extends X509UserNameRetriever>) Class.forName(x509UserNameRetrieverClassName);
1493
				x509UserNameRetriever = x509UserNameRetrieverClass.newInstance();
1494
				created = true;
1495
				if (log.isDebugEnabled()) {
1496
					log.debug("Success to create x509UserNameRetriever [" + x509UserNameRetriever + "].");
1497
				}
1498
			} catch (ClassCastException e) {
1499
				String warnString = "Class [" + x509UserNameRetrieverClassName + "] is not instance of [" + X509UserNameRetriever.class.getSimpleName() + "].";
1500
				log.warn(warnString);
1501
			} catch (ClassNotFoundException e) {
1502
				String warnString = "Class [" + x509UserNameRetrieverClassName + "] was not found.";
1503
				log.warn(warnString, e);
1504
			} catch (InstantiationException e) {
1505
				String warnString = "Cannot instantiate class [" + x509UserNameRetrieverClassName + "].";
1506
				log.warn(warnString);
1507
			} catch (IllegalAccessException e) {
1508
				String warnString = "Cannot instantiate class [" + x509UserNameRetrieverClassName + "].";
1509
				log.warn(warnString);
1510
			}
1511
		}
1512
		return created;
1513
	}
1514
1515
	
1516
	
1517
	
1518
    
1519
1394
}
1520
}
(-)org/apache/catalina/realm/X509UserNameRetriever.java (+30 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
package org.apache.catalina.realm;
19
20
import java.security.cert.X509Certificate;
21
22
/**
23
 * The interface <b>X509UserNameRetriever</b> defines how to retrieve a user name from X509Certificate.
24
 * The user name is the unique part of information from the client certificate 
25
 * that used to identify the identity of the user. 
26
 * The interface is used to get the user name during the client certificate authentication
27
 */
28
public interface X509UserNameRetriever {
29
	String getUserName(X509Certificate clientCert);
30
}

Return to bug 52500