Writing Apache Modules with Perl and C
By:   Lincoln Stein and Doug MacEachern
Published:   O'Reilly & Associates, Inc.  - March 1999

Copyright © 1999 by O'Reilly & Associates, Inc.


 


   Show Contents   Previous Page   Next Page

Chapter 10 - C API Reference Guide, Part I / Server Core Routines
Authorization and Authentication Routines

The last core routines we'll consider are those used for access control, authentication, and authorization. If you are familiar with the Perl API from Chapter 6, you'll find no surprises here.

These routines are declared in http_core.h unless otherwise specified:

int ap_allow_options (request_rec *r)

The ap_allow_options() call returns a bit mask containing the contents of the Perl-directory Options directive. You can logically AND this bit mask with a set of symbolic constants in order to determine which options are set. For example, the following code fragment checks whether ExecCGI is active for the directory containing the currently requested document:

if(!(ap_allow_options(r) & OPT_EXECCGI)) {
    ap_log_reason("Options ExecCGI is off in this directory",
                  $r->filename, r);
    return HTTP_FORBIDDEN;
}

The options constants are as follows:

Constant
Meaning
OPT_INDEXES
The Indexes option is set.
OPT_INCLUDES
The Includes option is set.
OPT_SYM_
LINKS
The SymLinks option is set.
OPT_EXECCGI
The ExecCGI option is set.
OPT_UNSET
(See the description that follows.)
OPT_
INCNOEXEC
The IncludeNoExec option is set.
OPT_SYM_
OWNER
The SymLinksIfOwnerMatch option is set.
OPT_MULTI
The MultiViews option is set.

Also available are the constants OPT_NONE, for no options set (this is defined as zero), and OPT_ALL, for all but the MultiViews option set.

OPT_UNSET corresponds to a bit that is initially set to 1 in the options flag but is not otherwise used. If no absolute assignment to the Options directive has been made, then this bit will remain set; otherwise, it will be unset. In other words, you can test this bit to determine whether only additive and subtractive assignments to Options have been made. In a directory with this Options directive, the OPT_UNSET bit will be true:

Options +ExecCGI -Indexes

However, in a directory with this directive, the bit will be false:

Options ExecCGI

As Commander Spock would say, "Fascinating."

const char *ap_auth_name (request_rec *r)

If authentication is configured for the currently requested document or directory, ap_ auth_name() will return the name of the current authentication realm, as defined by the AuthName directive. If no realm is currently defined, this function will return NULL.

Note that it is quite possible for the current request to have an authentication realm without authentication actually being active. For example, there may be no requires directive in the directory configuration.

const char *auth_name = ap_auth_name(r);

const char *ap_auth_type (request_rec *r)

This call returns the type of authentication configured for the current file or directory or NULL if none. The current possibilities are Basic and Digest.

const char *auth_type = ap_auth_type(r);
if(strcasecmp(auth_type, "basic")) {
    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARN, r->server,
                 "%s can't handle AuthType %s", __FILE__, auth_type);
   return DECLINED;
}

Although the information returned by ap_auth_type() seems redundant with the contents of the connection record's ap_auth_type field, there is an important difference. ap_auth_type() returns the authentication scheme configured for the current directory, whereas the connection record's ap_auth_type field returns the authentication scheme only if authentication is actually in use. To determine whether authentication is active, you should only trust the connection record's field.

int ap_get_basic_auth_pw (request_rec *r, const char **pw)

If the browser provided password authentication in making its request, the ap_get_basic_ auth_pw() call will return the password. You pass the function the request record in r and the address of a character pointer in pw. If successful, the function will return a result code of OK and place a copy of the password in pw. Otherwise, the function will return one of the result codes DECLINED, HTTP_INTERNAL_SERVER_ERROR, or HTTP_ UNAUTHORIZED. DECLINED is returned when the current request isn't for a directory that is protected by Basic authentication. HTTP_INTERNAL_SERVER_ERROR can occur when the authorization realm directive is missing. Finally, HTTP_ UNAUTHORIZED is returned if the browser fails to provide a password or attempts to use the wrong authentication scheme.

This call is typically used by authentication handlers to recover the user's password. The username can be retrieved from the connection record's user field. You should then do something with the two values to validate them.

const char *sent_pw = NULL;
char *user;
int ret = ap_get_basic_auth_pw(r, &sent_pw);
if(ret != OK) {
   return ret;
}
user = r->connection->user;
...

void ap_note_basic_auth_failure (request_rec *r)
void ap_note_digest_auth_failure (request_rec *r)
void ap_note_auth_failure (request_rec *r)

(Declared in the header file http_protocol.h.) If authentication is required for the current directory, but the browser did not provide the required information, these three variants set the HTTP authentication gears in motion by sending an "Authentication Required" message to the browser.

ap_note_basic_auth_failure() and ap_note_digest_auth_failure() are used for Basic and Digest authentication schemes, respectively. The generic ap_note_auth_failure() call will dispatch to one of those two routines based on which type of authentication the current directory is configured to use.

We can now write the skeleton for username/password authentication. In this example, check_auth() is some routine that you provide to check that the login name and password are valid. Replace this routine with a function that always returns 1, and you have our Apache::AuthAny module from Chapter 6!

const char *sent_pw = NULL;
char *user = r->connection->user;
int ret = ap_get_basic_auth_pw(r, &sent_pw);
if (ret != OK) {
   return ret;
}
if(!(user && sent_pwd && check_auth(user, sent_pw)) {
   ap_note_basic_auth_failure(r);
   ap_log_reason("User did not authenticate", r->uri, r);
   return HTTP_UNAUTHORIZED;
}

const array_header *ap_requires (request_rec *r)

As we described in Chapter 6, after a successful authentication, Apache calls the authorization handler to determine whether the authenticated user is allowed access to the requested document. To do this, the authorization handler needs to process any and all requires directives in the current directory configuration. The ap_requires() call returns the contents of these directives in predigested form.

The function result of ap_requires() is an array_header* containing a list of require_line structs. The definition of this data type, as found in http_core.h, is as follows:

typedef struct {
   int method_mask;
   char *requirement;
} require_line;

method_mask is an integer bitmask constructed from the request methods listed in the current <Limit> directive, or -1 if no <Limit> section applies. The set bit numbers correspond to the method numbers M_GET, M_POST, and so on. For example, you could determine whether the first requirement applies to POST requests with the following code fragment:

int isPost = 0 != (requirement[0].method_mask & (1 << M_POST));

requirement is a character string containing the exact text of the requires directive. You will need to parse this text in order to determine what type of requirement to apply.

Example 10-6 gives a short example of iterating over the ap_requires() array and printing out the information it contains. You should be able to use this code in a real authorization module by replacing the various print statements with code that performs the actual authorization checks. For real-life examples, see mod_auth, mod_auth_dbm, and the other standard authorization modules.

Example 10-6. Processing requires Directives

static char *request_methods[] = {
  "GET","PUT","POST","DELETE","CONNECT","OPTIONS","TRACE",NULL
};
#define comma_or_newline(value) \
if(value) fprintf(stderr, ", "); \
else      fprintf(stderr, "\n");
static void hello_util_requires_dump(request_rec *r)
{
  const array_header *requires = ap_requires(r);
  require_line *rq;
  int x;
   if (!requires) {
      fprintf(stderr,
              "requires: there are no requirements for this request\n"); 
return; }
   rq = (require_line *) requires->elts;
   for (x = 0; x < requires->nelts; x++) {
      const char *line, *requirement;
      int i;
       fprintf(stderr, "requires: limited to request methods: ");
      for(i=0; request_methods[i]; i++) {
          if (rq[x].method_mask & (1 << i))
              fprintf(stderr, "%s ", request_methods[i]);
      }
      fprintf(stderr, "\n");
       line = rq[x].requirement;
      requirement = ap_getword(r->pool, &line, ' ');
       if (!strcmp(requirement, "valid-user")) {
          fprintf(stderr, "requires: any valid-user allowed here.\n");
          return;
      }
       if (!strcmp(requirement, "user")) {
          fprintf(stderr, "requires: allowed users: ");
          while (line[0]) {
              requirement = ap_getword_conf(r->pool, &line);
              fprintf(stderr, "`%s'", requirement);
              comma_or_newline(line[0]);
          }
      }
       else if (!strcmp(requirement, "group")) {
          fprintf(stderr, "requires: allowed groups: ");
          while (line[0]) {
              requirement = ap_getword_conf(r->pool, &line);
              fprintf(stderr, "`%s'", requirement);
              comma_or_newline(line[0]);
          }
      }
  }
}

int ap_satisfies (request_rec *r)

The Satisfy directive determines whether a request for a URI that is protected by both access control and authentication must pass through both phases successfully or either one or the other. If Satisfy is set to all, all access control and authentication tests must be passed successfully. In contrast, if the directive is set to any, then the request will be allowed if any of the checks returns OK.

Handlers involved with access control can gain access to this configuration directive using the ap_satisfies() function. It returns one of the constants SATISFY_ANY, SATISFY_ALL, or SATISFY_NOSPEC. The last constant indicates that the directive wasn't present at all. Each of these constants, and the declaration of ap_satisfies() itself, is found in http_core.h.

As an example, consider an access control handler that wants to write an error log message when a user is denied access, but not when SATISFY_ANY is set, because the user might still be allowed in during the authentication phase. It can do the following:

if (return_value == HTTP_FORBIDDEN) {
  if (!(r->satisfies == SATISFY_ANY && ap_some_auth_required(r)))
    ap_log_reason("Client denied by server configuration", r->uri, r);
}
return return_value;

int ap_some_auth_required (request_rec *r)

The ap_some_auth_required() function can be used within any handler to determine whether authentication is required for the requested document. If you are writing a module that must always run with authentication enabled (such as a password changing program), you can use this call to make sure that the module is never inadvertently run without protection. For example:

if(!ap_some_auth_required(r)) {
   ap_log_reason("I won't go further unless the user is authenticated",
                 r->uri, r);
   return HTTP_FORBIDDEN;
}

The next chapter shows you how to create configuration directives with the C API and covers less frequently used parts of the C-language API.

Footnotes

4 When native syslog support is enabled, the stderr stream will be redirected to /dev/null!

5 In fact, the error log API maps directly to syslog when native syslog support is enabled. See the Apache documentation on the ErrorLog directive for details on enabling native syslog support.

6 Reliable piped log support was not available on Win32 platforms at the time this was written.    Show Contents   Previous Page   Next Page
Copyright © 1999 by O'Reilly & Associates, Inc.

HIVE: All information for read only. Please respect copyright!
Hosted by hive КГБ: Киевская городская библиотека