/************************************************************************* * * * EJBCA Community: The OpenSource Certificate Authority * * * * This software is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or any later version. * * * * See terms of license at gnu.org. * * * *************************************************************************/ package org.ejbca.core.ejb.ra; import java.math.BigInteger; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.ejb.EJB; import javax.ejb.Stateless; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.cesecore.authentication.tokens.AlwaysAllowLocalAuthenticationToken; import org.cesecore.authentication.tokens.AuthenticationToken; import org.cesecore.authentication.tokens.UsernamePrincipal; import org.cesecore.authorization.AuthorizationDeniedException; import org.cesecore.authorization.AuthorizationSessionLocal; import org.cesecore.authorization.control.StandardRules; import org.cesecore.certificates.ca.CADoesntExistsException; import org.cesecore.certificates.ca.CaSessionLocal; import org.cesecore.certificates.certificate.CertificateConstants; import org.cesecore.certificates.certificate.CertificateStoreSessionLocal; import org.cesecore.certificates.certificate.CertificateWrapper; import org.cesecore.certificates.endentity.EndEntityConstants; import org.cesecore.certificates.endentity.EndEntityInformation; import org.cesecore.config.GlobalCesecoreConfiguration; import org.cesecore.configuration.GlobalConfigurationSessionLocal; import org.cesecore.jndi.JndiConstants; import org.cesecore.util.CertTools; import org.cesecore.util.EJBTools; import org.cesecore.util.StringTools; import org.ejbca.config.GlobalConfiguration; import org.ejbca.core.EjbcaException; import org.ejbca.core.ejb.ra.raadmin.EndEntityProfileSessionLocal; import org.ejbca.core.model.InternalEjbcaResources; import org.ejbca.core.model.SecConst; import org.ejbca.core.model.authorization.AccessRulesConstants; import org.ejbca.core.model.ra.NotFoundException; import org.ejbca.core.model.ra.RAAuthorization; import org.ejbca.util.crypto.SupportedPasswordHashAlgorithm; import org.ejbca.util.query.BasicMatch; import org.ejbca.util.query.IllegalQueryException; import org.ejbca.util.query.Query; import org.ejbca.util.query.UserMatch; /** * @version $Id: EndEntityAccessSessionBean.java 29301 2018-06-21 10:27:38Z andresjakobs $ * */ @Stateless(mappedName = JndiConstants.APP_JNDI_PREFIX + "EndEntityAccessSessionRemote") @TransactionAttribute(TransactionAttributeType.SUPPORTS) public class EndEntityAccessSessionBean implements EndEntityAccessSessionLocal, EndEntityAccessSessionRemote { /** Columns in the database used in select. */ private static final String USERDATA_CREATED_COL = "timeCreated"; private static final Logger log = Logger.getLogger(EndEntityAccessSessionBean.class); /** Internal localization of logs and errors */ private static final InternalEjbcaResources intres = InternalEjbcaResources.getInstance(); @PersistenceContext(unitName = "ejbca") private EntityManager entityManager; @EJB private AuthorizationSessionLocal authorizationSession; @EJB private CaSessionLocal caSession; @EJB private EndEntityProfileSessionLocal endEntityProfileSession; @EJB private GlobalConfigurationSessionLocal globalConfigurationSession; @EJB private CertificateStoreSessionLocal certificateStoreSession; @TransactionAttribute(TransactionAttributeType.SUPPORTS) @Override public AbstractMap.SimpleEntry getPasswordAndHashAlgorithmForUser(String username) throws NotFoundException { UserData user = findByUsername(username); if (user == null) { throw new NotFoundException("End Entity of name " + username + " not found in database"); } else { return new AbstractMap.SimpleEntry(user.getPasswordHash(), user.findHashAlgorithm()); } } @TransactionAttribute(TransactionAttributeType.SUPPORTS) @Override public List findUserBySubjectDN(final AuthenticationToken admin, final String subjectdn) throws AuthorizationDeniedException { if (log.isTraceEnabled()) { log.trace(">findUserBySubjectDN(" + subjectdn + ")"); } // String used in SQL so strip it final String dn = CertTools.stringToBCDNString(StringTools.strip(subjectdn)); if (log.isDebugEnabled()) { log.debug("Looking for users with subjectdn: " + dn); } final TypedQuery query = entityManager.createQuery("SELECT a FROM UserData a WHERE a.subjectDN=:subjectDN", UserData.class); query.setParameter("subjectDN", dn); final List dataList = query.getResultList(); if (dataList.size() == 0) { if (log.isDebugEnabled()) { log.debug("Cannot find user with subjectdn: " + dn); } } final List result = new ArrayList(); for (UserData data : dataList) { result.add(convertUserDataToEndEntityInformation(admin, data, null)); } if (log.isTraceEnabled()) { log.trace(" findNewOrKeyrecByHardTokenIssuerId(int hardTokenIssuerId, int maxResults) { final TypedQuery query = entityManager .createQuery("SELECT a FROM UserData a WHERE a.hardTokenIssuerId=:hardTokenIssuerId AND a.tokenType>=:tokenType AND (a.status=:status1 OR a.status=:status2)", UserData.class); query.setParameter("hardTokenIssuerId", hardTokenIssuerId); query.setParameter("tokenType", SecConst.TOKEN_HARD_DEFAULT); query.setParameter("status1", EndEntityConstants.STATUS_NEW); query.setParameter("status2", EndEntityConstants.STATUS_KEYRECOVERY); if (maxResults > 0) { query.setMaxResults(maxResults); } return query.getResultList(); } @TransactionAttribute(TransactionAttributeType.SUPPORTS) @Override public List findSubjectDNsByCaIdAndNotUsername(final int caId, final String username, final String serialnumber) { final TypedQuery query = entityManager .createQuery("SELECT a.subjectDN FROM UserData a WHERE a.caId=:caId AND a.username!=:username AND a.subjectDN LIKE :serial", String.class); query.setParameter("caId", caId); query.setParameter("username", username); query.setParameter("serial", "%SN="+ serialnumber + "%"); return query.getResultList(); } @TransactionAttribute(TransactionAttributeType.SUPPORTS) @Override public List findUserBySubjectAndIssuerDN(final AuthenticationToken admin, final String subjectdn, final String issuerdn) throws AuthorizationDeniedException { if (log.isTraceEnabled()) { log.trace(">findUserBySubjectAndIssuerDN(" + subjectdn + ", " + issuerdn + ")"); } // String used in SQL so strip it final String dn = CertTools.stringToBCDNString(StringTools.strip(subjectdn)); final String issuerDN = CertTools.stringToBCDNString(StringTools.strip(issuerdn)); if (log.isDebugEnabled()) { log.debug("Looking for users with subjectdn: " + dn + ", issuerdn : " + issuerDN); } final TypedQuery query = entityManager.createQuery("SELECT a FROM UserData a WHERE a.subjectDN=:subjectDN AND a.caId=:caId", UserData.class); query.setParameter("subjectDN", dn); query.setParameter("caId", issuerDN.hashCode()); final List dataList = query.getResultList(); if (dataList.size() == 0) { if (log.isDebugEnabled()) { log.debug("Cannot find user with subjectdn: " + dn + ", issuerdn : " + issuerDN); } } final List result = new ArrayList(); for (UserData data : dataList) { result.add(convertUserDataToEndEntityInformation(admin, data, null)); } if (log.isTraceEnabled()) { log.trace("findUser(" + username + ")"); } final UserData data = findByUsername(username); if (data == null) { if (log.isDebugEnabled()) { log.debug("Cannot find user with username='" + username + "'"); } } final EndEntityInformation ret = convertUserDataToEndEntityInformation(admin, data, username); if (log.isTraceEnabled()) { log.trace(" findUserByEmail(AuthenticationToken admin, String email) throws AuthorizationDeniedException { if (log.isTraceEnabled()) { log.trace(">findUserByEmail(" + email + ")"); } if (log.isDebugEnabled()) { log.debug("Looking for user with email: " + email); } final TypedQuery query = entityManager.createQuery("SELECT a FROM UserData a WHERE a.subjectEmail=:subjectEmail", UserData.class); query.setParameter("subjectEmail", email); final List result = query.getResultList(); if (result.size() == 0) { if (log.isDebugEnabled()) { log.debug("Cannot find user with Email='" + email + "'"); } } final List returnval = new ArrayList(); for (final UserData data : result) { if (((GlobalConfiguration) globalConfigurationSession.getCachedConfiguration(GlobalConfiguration.GLOBAL_CONFIGURATION_ID)) .getEnableEndEntityProfileLimitations()) { // Check if administrator is authorized to view user. if (!authorizedToEndEntityProfile(admin, data.getEndEntityProfileId(), AccessRulesConstants.VIEW_END_ENTITY)) { continue; } } if (!authorizedToCA(admin, data.getCaId())) { continue; } returnval.add(convertUserDataToEndEntityInformation(admin, data, null)); } if (log.isTraceEnabled()) { log.trace(" findAllUsersByStatus(AuthenticationToken admin, int status) { if (log.isTraceEnabled()) { log.trace(">findAllUsersByStatus(" + status + ")"); } if (log.isDebugEnabled()) { log.debug("Looking for users with status: " + status); } Query query = new Query(Query.TYPE_USERQUERY); query.add(UserMatch.MATCH_WITH_STATUS, BasicMatch.MATCH_TYPE_EQUALS, Integer.toString(status)); Collection returnval = null; try { returnval = query(admin, query, null, null, 0, AccessRulesConstants.VIEW_END_ENTITY); } catch (IllegalQueryException e) { } if (log.isDebugEnabled()) { log.debug("found " + returnval.size() + " user(s) with status=" + status); } if (log.isTraceEnabled()) { log.trace(" findAllUsersByCaId(AuthenticationToken admin, int caid) { if (log.isTraceEnabled()) { log.trace(">findAllUsersByCaId(" + caid + ")"); } if (log.isDebugEnabled()) { log.debug("Looking for users with caid: " + caid); } Query query = new Query(Query.TYPE_USERQUERY); query.add(UserMatch.MATCH_WITH_CA, BasicMatch.MATCH_TYPE_EQUALS, Integer.toString(caid)); Collection returnval = null; try { returnval = query(admin, query, null, null, 0, AccessRulesConstants.VIEW_END_ENTITY); } catch (IllegalQueryException e) { // Ignore ?? log.debug("Illegal query", e); returnval = new ArrayList(); } if (log.isDebugEnabled()) { log.debug("found " + returnval.size() + " user(s) with caid=" + caid); } if (log.isTraceEnabled()) { log.trace("=:tokenType AND (a.status=:status1 OR a.status=:status2)"); query.setParameter("hardTokenIssuerId", hardTokenIssuerId); query.setParameter("tokenType", SecConst.TOKEN_HARD_DEFAULT); query.setParameter("status1", EndEntityConstants.STATUS_NEW); query.setParameter("status2", EndEntityConstants.STATUS_KEYRECOVERY); return ((Long) query.getSingleResult()).longValue(); // Always returns a result } @TransactionAttribute(TransactionAttributeType.SUPPORTS) @Override public List findAllBatchUsersByStatusWithLimit(int status) { if (log.isTraceEnabled()) { log.trace(">findAllUsersByStatusWithLimit()"); } final javax.persistence.Query query = entityManager .createQuery("SELECT a FROM UserData a WHERE a.status=:status AND (clearPassword IS NOT NULL)"); query.setParameter("status", status); query.setMaxResults(getGlobalCesecoreConfiguration().getMaximumQueryCount()); @SuppressWarnings("unchecked") final List userDataList = query.getResultList(); final List returnval = new ArrayList(userDataList.size()); for (UserData ud : userDataList) { EndEntityInformation endEntityInformation = ud.toEndEntityInformation(); if (endEntityInformation.getPassword() != null && endEntityInformation.getPassword().length() > 0) { returnval.add(endEntityInformation); } } if (log.isTraceEnabled()) { log.trace(" query(final AuthenticationToken admin, final Query query, final String caauthorizationstr, final String endentityprofilestr, final int numberofrows, final String endentityAccessRule) throws IllegalQueryException { boolean authorizedtoanyprofile = true; final String caauthorizationstring = StringTools.strip(caauthorizationstr); final String endentityprofilestring = StringTools.strip(endentityprofilestr); final ArrayList returnval = new ArrayList(); int fetchsize = getGlobalCesecoreConfiguration().getMaximumQueryCount(); if (numberofrows != 0) { fetchsize = numberofrows; } // Check if query is legal. if (query != null && !query.isLegalQuery()) { throw new IllegalQueryException(); } String sqlquery = ""; if (query != null) { sqlquery += query.getQueryString(); } final GlobalConfiguration globalconfiguration = getGlobalConfiguration(); String caauthstring = caauthorizationstring; String endentityauth = endentityprofilestring; RAAuthorization raauthorization = null; if (caauthorizationstring == null || endentityprofilestring == null) { raauthorization = new RAAuthorization(admin, globalConfigurationSession, authorizationSession, caSession, endEntityProfileSession); caauthstring = raauthorization.getCAAuthorizationString(); if (globalconfiguration.getEnableEndEntityProfileLimitations()) { endentityauth = raauthorization.getEndEntityProfileAuthorizationString(true, endentityAccessRule); } else { endentityauth = ""; } } if (!StringUtils.isBlank(caauthstring)) { if (StringUtils.isBlank(sqlquery)) { sqlquery += caauthstring; } else { sqlquery = "(" + sqlquery + ") AND " + caauthstring; } } if (globalconfiguration.getEnableEndEntityProfileLimitations()) { if (endentityauth == null || StringUtils.isBlank(endentityauth)) { authorizedtoanyprofile = false; } else { if (StringUtils.isEmpty(sqlquery)) { sqlquery += endentityauth; } else { sqlquery = "(" + sqlquery + ") AND " + endentityauth; } } } // Finally order the return values sqlquery += " ORDER BY " + USERDATA_CREATED_COL + " DESC"; if (log.isDebugEnabled()) { log.debug("generated query: " + sqlquery); } if (authorizedtoanyprofile) { final javax.persistence.Query dbQuery = entityManager.createQuery("SELECT a FROM UserData a WHERE " + sqlquery); if (fetchsize > 0) { dbQuery.setMaxResults(fetchsize); } @SuppressWarnings("unchecked") final List userDataList = dbQuery.getResultList(); for (UserData userData : userDataList) { returnval.add(userData.toEndEntityInformation()); } } else { if (log.isDebugEnabled()) { log.debug("authorizedtoanyprofile=false"); } } if (log.isTraceEnabled()) { log.trace(" findAllUsersWithLimit(AuthenticationToken admin) { if (log.isTraceEnabled()) { log.trace(">findAllUsersWithLimit()"); } Collection returnval = null; try { returnval = query(admin, null, null, null, 0, AccessRulesConstants.VIEW_END_ENTITY); } catch (IllegalQueryException e) { } if (log.isTraceEnabled()) { log.trace(" findAllUsersByCaIdNoAuth(int caid) { if (log.isTraceEnabled()) { log.trace(">findAllUsersByCaIdNoAuth()"); } final TypedQuery query = entityManager.createQuery("SELECT a FROM UserData a WHERE a.caId=:caId", UserData.class); query.setParameter("caId", caid); final List userDataList = query.getResultList(); final List returnval = new ArrayList(userDataList.size()); for (UserData ud : userDataList) { returnval.add(ud.toEndEntityInformation()); } if (log.isTraceEnabled()) { log.trace(" findByEndEntityProfileId(int endentityprofileid) { if (log.isTraceEnabled()) { log.trace(">findByEndEntityProfileId(" + endentityprofileid + ")"); } final TypedQuery query = entityManager.createQuery("SELECT a FROM UserData a WHERE a.endEntityProfileId=:endEntityProfileId", UserData.class); query.setParameter("endEntityProfileId", endentityprofileid); List found = query.getResultList(); if (log.isTraceEnabled()) { log.trace(" findByCertificateProfileId(int certificateprofileid) { if (log.isTraceEnabled()) { log.trace(">checkForCertificateProfileId("+certificateprofileid+")"); } final javax.persistence.Query query = entityManager.createQuery("SELECT a FROM UserData a WHERE a.certificateProfileId=:certificateProfileId"); query.setParameter("certificateProfileId", certificateprofileid); List result = new ArrayList(); for(Object userDataObject : query.getResultList()) { result.add(((UserData) userDataObject).getUsername()); } if (log.isTraceEnabled()) { log.trace(" findCertificatesByUsername(final AuthenticationToken authenticationToken, final String username, final boolean onlyValid, final long now) throws AuthorizationDeniedException, CertificateEncodingException { if (log.isDebugEnabled()) { log.debug( "Find certificates by username requested by " + authenticationToken.getUniqueId()); } // Check authorization on current CA and profiles and view_end_entity by looking up the end entity. if (findUser(authenticationToken, username) == null) { if (log.isDebugEnabled()) { log.debug(intres.getLocalizedMessage("ra.errorentitynotexist", username)); } } // Even if there is no end entity, it might be the case that we don't store UserData, so we still need to check CertificateData. Collection searchResults; if (onlyValid) { // We will filter out not yet valid certificates later on, but we as the database to not return any expired certificates searchResults = EJBTools.wrapCertCollection(certificateStoreSession.findCertificatesByUsernameAndStatusAfterExpireDate(username, CertificateConstants.CERT_ACTIVE, now)); } else { searchResults = certificateStoreSession.findCertificatesByUsername(username); } // Assume the user may have certificates from more than one CA. Certificate certificate = null; int caId = -1; Boolean authorized = null; final Map authorizationCache = new HashMap<>(); final List result = new ArrayList<>(); for (Object searchResult: searchResults) { certificate = ((CertificateWrapper) searchResult).getCertificate(); caId = CertTools.getIssuerDN(certificate).hashCode(); authorized = authorizationCache.get(caId); if (authorized == null) { authorized = authorizationSession.isAuthorizedNoLogging(authenticationToken, StandardRules.CAACCESS.resource() + caId); authorizationCache.put(caId, authorized); } if (authorized.booleanValue()) { result.add((CertificateWrapper) searchResult); } } if (log.isDebugEnabled()) { log.debug( "Found " + result.size() + " certificate(s) by username requested by " + authenticationToken.getUniqueId()); } return result; } }