Show Contents Previous Page Next Page
Chapter 6 - Authentication and Authorization / Authentication with the Secure Sockets Layer Using Digital Certificates for Authorization
The SSL protocol does most of its work at a level beneath the workings of the HTTP protocol. The exchange and verificaton of digital certificates and the establishment of the encrypted channel all occur before any of Apache's handlers run. For this reason, authorization based on the contents of a digital certificate looks quite different from the other examples we've seen in this chapter. Furthermore, the details of authorization vary slightly among the different implementations of ApacheSSL. This section describes the way it works in Ralf S. Engelschall's mod_ssl. If you are using a different version of ApacheSSL, you should check your vendor's documentation for differences.
The text representation of a typical client certificate is shown in Example 6-15.
It consists of a "Subject" section, which gives information on the person to
whom the certificate is issued, and a "Certificate" section, which gives information
about the certificate itself. Within the Subject section are a series of tag=value
pairs. There can be an arbitrary number of such pairs, but several are standard
and can be found in any certificate:
CN
|
User's common name
| EMail
|
User's email address
| O
|
User's organization (employer)
| OU
|
Organizational unit (e.g., department)
| L
|
User's locality, usually a city or town
| SP
|
User's state or province
| C
|
User's country code
|
The user's distinguished name (DN) is a long string consisting of the concatenation of each of these fields in the following format:
/C=US/SP=MA/L=Boston/O=Capricorn Organization/OU=Sales/CN=Wanda/Email=wanda@capricorn.com
European users will recognize the footprints of the OSI standards committee here. The DN is guaranteed to be unique among all the certificates issued by a particular certificate-granting authority.
The Certificate section contains the certificate's unique serial number and other data, followed by more tag=value pairs giving information about the organization issuing the certificate. The standard fields are the same as those described for the Subject. This is followed by a Validity period, which gives the span of time that the certificate should be considered valid.
You are free to use any of these fields for authorization. You can authorize based on the user's CN field, on the certificate's serial number, on the Validity period, on the DN, or on any of the Subject or Issuer tags.
The certificate information is actually stored in a compact binary form rather than the text form shown here. When the connection is established, the SSL library parses out the certificate fields and stores them in a private data structure. During the fixup phase, these fields are turned into various environment variables with names like SSL_CLIENT_S_DN_CN (to be read as "the common name subfield of the distinguished name of the subject section of the client's certificate"). However, the mappings between certificate field and environment variable differ from version to version of ApacheSSL, and you will have to check your vendor's documentation for the details.
Example 6-15. An Example Client Certificate
Subject:
C=US
SP=MA
L=Boston
O=Capricorn Organization
OU=Sales
CN=Wanda
Email=wanda@capricorn.com
Certificate:
Data:
Version: 1 (0x0)
Serial Number: 866229881 (0x33a19e79)
Signature Algorithm: md5WithRSAEncryption Issuer:
C=US
SP=MA
L=Boston
O=Capricorn Consulting
OU=Security Services
CN=Capricorn Signing Services Root CA
Email=lstein@capricorn.com
Validity:
Not Before: Jun 13 19:24:41 1998 GMT Not After : Jun 13 19:24:41 1999 GMT
The most straightforward way to authenticate based on certificate information is to take advantage of the SSLRequire access control directive. In mod_ssl, such a directive might look like this:
<Location /certified>
SSLRequire %{SSL_CLIENT_S_DN_CN} in ("Wanda Henderson","Joe Bloe") \
and %{REMOTE_ADDR} =~ m/^192\.128\.3\.[0-9]+$/
</Location>
This requires that the CN tag of the DN field of the Subject section of the certificate match either "Wanda Henderson" or "Joe Bloe", and that the browser's IP address satisfy a pattern match placing it within the 192.128.3 subnetwork. mod_ssl has a rich language for querying the contents of the client certificate. See its documentation for the details. Other ApacheSSL implementations also support operations similar to SSLRequire, but they differ somewhat in detail.
Note that to Apache, SSLRequire is an access control operation rather than an authentication/authorization operation. This is because no action on the part of the user is needed to gain access--his browser either has the right certificate, or it doesn't.
A slightly more involved technique for combining certificate information with user authorization is to take advantage of the FakeBasicAuth option of the SSLOptions directive. When this option is enabled, mod_ssl installs an authentication handler that retrieves the DN from the certificate. The handler base64-encodes the DN and a hardcoded password (consisting of the string "password"), stuffs them into the incoming Authorization header field, and returns DECLINED . In effect, this fakes the ordinary Basic authentication process by making it seem as if the user provided a username and password pair. The DN is now available for use by downstream authentication and authorization modules, where it appears as the username.
However, using FakeBasicAuth means that mod_ssl must be the first authentication handler run for the request and that an authentication handler further down the chain must be able to authenticate using the client's DN. It is much simpler to bypass all authentication handlers and obtain of the DN by using a subrequest. This takes advantage of the fact that during the fixup phase, mod_ssl places parsed copies of the certificate fields into the subprocess environment table, preparatory to copying them into a CGI script's environment.
As an example, we'll show a simple authorization module named Apache::AuthzSSL which checks that a named field of the DN name matches that given in one or more require directives. A typical configuration section will look like this:
SSLVerifyClient require
SSLVerifyDepth 2
SSLCACertificateFile conf/ssl.crt/ca-bundle.crt
<Directory /usr/local/apache/htdocs/ID/please>
SSLRequireSSL
AuthName SSL
AuthType Basic
PerlAuthenHandler Apache::OK
PerlAuthzHandler Apache::AuthzSSL
require C US
require O "Capricorn Organization"
require OU Sales Marketing
</Directory>
The SSLVerifyClient directive, which must be present in the main part of the configuration file, requires that browsers present client certificates. The SSLVerifyDepth and SSLCACertificateFile directives are used to configure how deeply mod_ssl should verify client certificates (see the mod_ssl documentation for details). The SSLRequireSSL directive requires that SSL be active in order to access the contents of this directory.
AuthName and AuthType are not required, since we are not
performing Basic authentication, but we put them in place just in case some
module downstream is expecting them. Since the password is invariant when client
certificate verification is in use, we bypass password checking by installing
Apache::OK as the authentication handler for this directory.11Apache::OK is a ring module that exits with an OK result
code. We then install Apache::AuthzSSL as the authorization handler
and give it three different require statements to satisfy. We require
that the country field equal "US," the organization field equal "Capricorn Organization,"
and the organizational unit be one of "Sales" or "Marketing." Example 6-16 gives the code for Apache::AuthzSSL.
It brings in Apache::Constants and the quotewords() text parsing
function from the standard Text::ParseWords module. It recovers the
request object and calls its requires() method to retrieve the list
of authorization requirements that are in effect.
The handler then issues a subrequest and retrieves the value of SSL_CLIENT_DN from the subrequest's environment table. The subrequest is necessary because the parsed certificate fields aren't placed into the table until the fixup stage, which ordinarily occurs after the authorization phase. Notice that the handler returns OK if is_main() returns false, avoiding infinite recursion during the subrequest. Once the DN is recovered, it is split into its individual fields using a pattern match operation.
Now the routine loops through each of the requirements, breaking them into a DN field name and a list of possible values, each of which it checks in turn. If none of the specified values matches the DN, we log an error and return a FORBIDDEN (not an AUTH_REQUIRED ) status code. If we satisfy all the requirements and fall through to the bottom of the loop, we return an OK result code.
Example 6-16. Authorizing Clients
Based On Their Digital Certificate's DN
package Apache::AuthzSSL;
use strict;
use Apache::Constants qw(:common);
use Text::ParseWords qw(quotewords);
sub handler {
my $r = shift;
return OK unless $r->is_main;
my $requires = $r->requires;
return DECLINED unless $requires;
my $subr = $r->lookup_uri($r->uri);
my $dn = $subr->subprocess_env('SSL_CLIENT_S_DN');
return DECLINED unless $dn;
my(%dn) = $dn =~ m{/([^=]+)=([^/]+)}g;
REQUIRES:
for my $entry (@$requires) {
my($field, @values) = quotewords('\s+', 0, $entry->{requirement});
foreach (@values) {
next REQUIRES if $dn{$field} eq $_;
}
$r->log_reason("user $dn{CN}: not authorized", $r->filename);
return FORBIDDEN;
}
# if we get here, then we passed all the requirements
return OK;
}
1;
__END__
The only subtlety in this module is the rationale for returning FORBIDDEN in an authorization module rather than in the more typical note_basic_auth_failure() call followed by AUTH_REQUIRED . The reason for this is that returning AUTH_REQUIRED will set in motion a chain of events that will ultimately result in the user being prompted for a username and password. But there's nothing the user can type in to satisfy this module's requirements, so this is just a tease. Returning FORBIDDEN , in contrast, will display a more accurate message denying the user permission to view the page.
A more advanced certificate authorization module would probably go to a database to determine whether the incoming certificate satisfied the requirements. As another example, Example 6-17 shows a small
access handler that rejects all certificates issued by out-of-state issuers.
It does so by looking at the value of the subprocess variable SSL_CLIENT_I_DN_SP ,
which returns the issuer's state or province code. This handler can be installed
with a configuration section like this one:
SSLVerifyClient require
<Location /government/local>
SSLRequireSSL
PerlAccessHandler Apache::CheckCertState
PerlSetVar IssuerState Maryland
</Location>
The code simply retrieves the contents of the IssuerState configuration variable and the SSL_CLIENT_I_DN_SP subprocess environment variable. If either is undefined, the handler returns DECLINED . Next the handler checks whether the two variables are equal, and if so, returns OK . Otherwise the routine returns FORBIDDEN , displaying the "access denied" message on the user's browser.
Example 6-17. Apache::CheckCertState
Checks the SP (State/Province) Field of the Certificate Issuer
package Apache::CheckCertState;
# file: Apache/CheckCertState.pm
use Apache::Constants qw(:common);
sub handler {
my $r = shift;
return DECLINED unless $r->is_main;
my $state = $r->dir_config('IssuerState');
return DECLINED unless defined $state;
my $subr = $r->lookup_uri($r->uri);
my $client_state = $subr->subprocess_env('SSL_CLIENT_I_DN_SP') || "";
return OK if $client_state eq $state;
return FORBIDDEN;
}
1;
__END__
We hope this chapter has given you some idea of the range and versatility of Apache modules for controlling who can gain access to your site and what they do once they've connected. With the tools and examples presented in this chapter as a starting point, you should be able to implement almost any access control system you can imagine.
The next chapter turns to some of the more esoteric handlers and module function-ality,
showing you a variety of techniques for simplifying Apache administration and
customizing the server's behavior. 11 Apache::OK is always available, along
with Apache::DECLINED, since they are imported from Apache::Constants
by Apache.pm at server startup time. Show Contents Previous Page Next Page Copyright © 1999 by O'Reilly & Associates, Inc. |