package org.zaproxy.zap.authentication; import java.awt.GridBagLayout; import java.awt.Insets; import java.net.InetAddress; import java.net.URI; import java.net.UnknownHostException; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.JLabel; import net.sf.json.JSONObject; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.httpclient.NTCredentials; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.log4j.Logger; import org.parosproxy.paros.Constant; import org.parosproxy.paros.db.DatabaseException; import org.parosproxy.paros.db.RecordContext; import org.parosproxy.paros.extension.ExtensionHook; import org.parosproxy.paros.model.Session; import org.zaproxy.zap.authentication.UsernamePasswordAuthenticationCredentials.UsernamePasswordAuthenticationCredentialsOptionsPanel; import org.zaproxy.zap.extension.api.ApiDynamicActionImplementor; import org.zaproxy.zap.extension.api.ApiException; import org.zaproxy.zap.extension.api.ApiResponse; import org.zaproxy.zap.extension.api.ApiResponseSet; import org.zaproxy.zap.extension.authentication.AuthenticationAPI; import org.zaproxy.zap.model.Context; import org.zaproxy.zap.session.SessionManagementMethod; import org.zaproxy.zap.session.WebSession; import org.zaproxy.zap.users.User; import org.zaproxy.zap.utils.ApiUtils; import org.zaproxy.zap.utils.ZapPortNumberSpinner; import org.zaproxy.zap.utils.ZapTextField; import org.zaproxy.zap.view.LayoutHelper; /** * The implementation for an {@link AuthenticationMethodType} where the Users are authenticated * through HTTP Authentication. * * @see <a href="http://www.w3.org/Protocols/HTTP/1.0/spec.html#AA">HTTP/1.0 - Access Authentication</a> */ public class HttpAuthenticationMethodType extends AuthenticationMethodType { public static final String CONTEXT_CONFIG_AUTH_HTTP = AuthenticationMethod.CONTEXT_CONFIG_AUTH + ".http"; public static final String CONTEXT_CONFIG_AUTH_HTTP_HOSTNAME = CONTEXT_CONFIG_AUTH_HTTP + ".hostname"; public static final String CONTEXT_CONFIG_AUTH_HTTP_REALM = CONTEXT_CONFIG_AUTH_HTTP + ".realm"; public static final String CONTEXT_CONFIG_AUTH_HTTP_PORT = CONTEXT_CONFIG_AUTH_HTTP + ".port"; private static final Logger log = Logger.getLogger(HttpAuthenticationMethodType.class); /** The unique identifier of the method. */ private static final int METHOD_IDENTIFIER = 3; /** The human readable Authentication method's name. */ private static final String METHOD_NAME = Constant.messages.getString("authentication.method.http.name"); /** The Authentication method's name used in the API. */ private static final String API_METHOD_NAME = "httpAuthentication"; /** * The implementation for an {@link AuthenticationMethodType} where the Users are authenticated * through HTTP Authentication. */ protected static class HttpAuthenticationMethod extends AuthenticationMethod { public HttpAuthenticationMethod() { super(); } protected String hostname; protected int port = 80; protected String realm; @Override public boolean isConfigured() { return hostname != null && !hostname.isEmpty() && realm != null && !realm.isEmpty(); } @Override protected AuthenticationMethod duplicate() { HttpAuthenticationMethod method = new HttpAuthenticationMethod(); method.hostname = this.hostname; method.port = this.port; method.realm = this.realm; return method; } @Override public AuthenticationCredentials createAuthenticationCredentials() { return new UsernamePasswordAuthenticationCredentials(); } @Override public AuthenticationMethodType getType() { return new HttpAuthenticationMethodType(); } @Override public WebSession authenticate(SessionManagementMethod sessionManagementMethod, AuthenticationCredentials credentials, User user) throws UnsupportedAuthenticationCredentialsException { WebSession session = user.getAuthenticatedSession(); if (session == null) session = sessionManagementMethod.createEmptyWebSession(); // type check if (!(credentials instanceof UsernamePasswordAuthenticationCredentials)) { throw new UnsupportedAuthenticationCredentialsException( "Form based authentication method only supports " + UsernamePasswordAuthenticationCredentials.class.getSimpleName()); } UsernamePasswordAuthenticationCredentials userCredentials = (UsernamePasswordAuthenticationCredentials) credentials; AuthScope stateAuthScope = null; NTCredentials stateCredentials = null; try { stateAuthScope = new AuthScope(this.hostname, this.port, (this.realm == null || this.realm.isEmpty()) ? AuthScope.ANY_REALM : this.realm); stateCredentials = new NTCredentials(userCredentials.getUsername(), userCredentials.getPassword(), InetAddress.getLocalHost().getCanonicalHostName(), this.hostname); session.getHttpState().setCredentials(stateAuthScope, stateCredentials); } catch (UnknownHostException e1) { log.error(e1.getMessage(), e1); } return session; } @Override public ApiResponse getApiResponseRepresentation() { Map<String, String> values = new HashMap<>(); values.put("methodName", API_METHOD_NAME); values.put("host", this.hostname); values.put("port", Integer.toString(this.port)); values.put("realm", this.realm); return new ApiResponseSet<String>("method", values); } } /** * The Options Panel used for configuring a {@link HttpAuthenticationMethod}. */ private static class HttpAuthenticationMethodOptionsPanel extends AbstractAuthenticationMethodOptionsPanel { private static final long serialVersionUID = 4341092284683481288L; private static final String HOSTNAME_LABEL = Constant.messages .getString("authentication.method.http.field.label.hostname"); private static final String PORT_LABEL = Constant.messages .getString("authentication.method.http.field.label.port"); private static final String REALM_LABEL = Constant.messages .getString("authentication.method.http.field.label.realm"); private ZapTextField hostnameField; private ZapTextField realmField; private ZapPortNumberSpinner portNumberSpinner; private HttpAuthenticationMethod method; public HttpAuthenticationMethodOptionsPanel() { super(); initialize(); } private void initialize() { this.setLayout(new GridBagLayout()); this.add(new JLabel(HOSTNAME_LABEL), LayoutHelper.getGBC(0, 0, 1, 0.0d)); this.hostnameField = new ZapTextField(); this.add(this.hostnameField, LayoutHelper.getGBC(1, 0, 1, 1.0d, new Insets(0, 0, 0, 10))); this.add(new JLabel(PORT_LABEL), LayoutHelper.getGBC(2, 0, 1, 0.0d)); this.portNumberSpinner = new ZapPortNumberSpinner(80); this.add(this.portNumberSpinner, LayoutHelper.getGBC(3, 0, 1, 0.0d)); this.add(new JLabel(REALM_LABEL), LayoutHelper.getGBC(0, 1, 1, 0.0d)); this.realmField = new ZapTextField(); this.add(this.realmField, LayoutHelper.getGBC(1, 1, 1, 1.0d, new Insets(0, 0, 0, 10))); } @Override public void validateFields() throws IllegalStateException { try { new URI(hostnameField.getText()); } catch (Exception ex) { hostnameField.requestFocusInWindow(); throw new IllegalStateException( Constant.messages.getString("authentication.method.http.dialog.error.url.text")); } } @Override public void saveMethod() { getMethod().hostname = hostnameField.getText(); getMethod().port = portNumberSpinner.getValue(); getMethod().realm = realmField.getText(); } @Override public void bindMethod(AuthenticationMethod method) throws UnsupportedAuthenticationMethodException { this.method = (HttpAuthenticationMethod) method; this.hostnameField.setText(this.method.hostname); this.portNumberSpinner.setValue(this.method.port); this.realmField.setText(this.method.realm); } @Override public HttpAuthenticationMethod getMethod() { return method; } } @Override public HttpAuthenticationMethod createAuthenticationMethod(int contextId) { return new HttpAuthenticationMethod(); } @Override public String getName() { return METHOD_NAME; } @Override public int getUniqueIdentifier() { return METHOD_IDENTIFIER; } @Override public AbstractAuthenticationMethodOptionsPanel buildOptionsPanel(Context uiSharedContext) { return new HttpAuthenticationMethodOptionsPanel(); } @Override public boolean hasOptionsPanel() { return true; } @Override public AbstractCredentialsOptionsPanel<? extends AuthenticationCredentials> buildCredentialsOptionsPanel( AuthenticationCredentials credentials, Context uiSharedContext) { return new UsernamePasswordAuthenticationCredentialsOptionsPanel( (UsernamePasswordAuthenticationCredentials) credentials); } @Override public boolean hasCredentialsOptionsPanel() { return true; } @Override public boolean isTypeForMethod(AuthenticationMethod method) { return (method instanceof HttpAuthenticationMethod); } @Override public void hook(ExtensionHook extensionHook) { // Nothing to hook } @Override public AuthenticationMethod loadMethodFromSession(Session session, int contextId) throws DatabaseException { HttpAuthenticationMethod method = createAuthenticationMethod(contextId); List<String> hostnames = session.getContextDataStrings(contextId, RecordContext.TYPE_AUTH_METHOD_FIELD_1); if (hostnames != null && hostnames.size() > 0) { method.hostname = hostnames.get(0); } List<String> realms = session .getContextDataStrings(contextId, RecordContext.TYPE_AUTH_METHOD_FIELD_2); if (realms != null && realms.size() > 0) { method.realm = realms.get(0); } List<String> ports = session.getContextDataStrings(contextId, RecordContext.TYPE_AUTH_METHOD_FIELD_3); if (ports != null && ports.size() > 0) { try { method.port = Integer.parseInt(ports.get(0)); } catch (Exception ex) { log.error("Unable to load HttpAuthenticationMethod. ", ex); } } return method; } @Override public void persistMethodToSession(Session session, int contextId, AuthenticationMethod authMethod) throws UnsupportedAuthenticationMethodException, DatabaseException { if (!(authMethod instanceof HttpAuthenticationMethod)) throw new UnsupportedAuthenticationMethodException("Http Authentication type only supports: " + HttpAuthenticationMethod.class); HttpAuthenticationMethod method = (HttpAuthenticationMethod) authMethod; session.setContextData(contextId, RecordContext.TYPE_AUTH_METHOD_FIELD_1, method.hostname); session.setContextData(contextId, RecordContext.TYPE_AUTH_METHOD_FIELD_2, method.realm); session.setContextData(contextId, RecordContext.TYPE_AUTH_METHOD_FIELD_3, Integer.toString(method.port)); } /* API related constants and methods. */ private static final String PARAM_HOSTNAME = "hostname"; private static final String PARAM_REALM = "realm"; private static final String PARAM_PORT = "port"; @Override public AuthenticationCredentials createAuthenticationCredentials() { return new UsernamePasswordAuthenticationCredentials(); } @Override public ApiDynamicActionImplementor getSetMethodForContextApiAction() { return new ApiDynamicActionImplementor(API_METHOD_NAME, new String[] { PARAM_HOSTNAME, PARAM_REALM }, new String[] { PARAM_PORT }) { @Override public void handleAction(JSONObject params) throws ApiException { Context context = ApiUtils.getContextByParamId(params, AuthenticationAPI.PARAM_CONTEXT_ID); HttpAuthenticationMethod method = createAuthenticationMethod(context.getIndex()); method.hostname = ApiUtils.getNonEmptyStringParam(params, PARAM_HOSTNAME); try { new URI(method.hostname); } catch (Exception ex) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_HOSTNAME); } if(params.containsKey(PARAM_REALM)) method.realm=params.getString(PARAM_REALM); if (params.containsKey(PARAM_PORT)) try { String portString = params.getString(PARAM_PORT); method.port = Integer.parseInt(portString); } catch (Exception ex) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_PORT); } if (!context.getAuthenticationMethod().isSameType(method)) apiChangedAuthenticationMethodForContext(context.getIndex()); context.setAuthenticationMethod(method); } }; } @Override public ApiDynamicActionImplementor getSetCredentialsForUserApiAction() { return UsernamePasswordAuthenticationCredentials.getSetCredentialsForUserApiAction(this); } @Override public void exportData(Configuration config, AuthenticationMethod authMethod) { if (!(authMethod instanceof HttpAuthenticationMethod)) { throw new UnsupportedAuthenticationMethodException( "HTTP based authentication type only supports: " + HttpAuthenticationMethod.class.getName()); } HttpAuthenticationMethod method = (HttpAuthenticationMethod) authMethod; config.setProperty(CONTEXT_CONFIG_AUTH_HTTP_HOSTNAME, method.hostname); config.setProperty(CONTEXT_CONFIG_AUTH_HTTP_REALM, method.realm); config.setProperty(CONTEXT_CONFIG_AUTH_HTTP_PORT, method.port); } @Override public void importData(Configuration config, AuthenticationMethod authMethod) throws ConfigurationException { if (!(authMethod instanceof HttpAuthenticationMethod)) { throw new UnsupportedAuthenticationMethodException( "HTTP based authentication type only supports: " + HttpAuthenticationMethod.class.getName()); } HttpAuthenticationMethod method = (HttpAuthenticationMethod) authMethod; method.hostname = config.getString(CONTEXT_CONFIG_AUTH_HTTP_HOSTNAME); method.realm = config.getString(CONTEXT_CONFIG_AUTH_HTTP_REALM); method.port = config.getInt(CONTEXT_CONFIG_AUTH_HTTP_PORT); } }