ML07-0002-0

Chapter 2. Routes and Authentication Policy

A route ties a path of a request to what HTTPD does with it. A route is written in the HTTPPRM member (Chapter 1, “The HTTPPRM Keywords”) as a MOD line or a LOC line. A MOD route names a program, a server module that HTTPD links to. A LOC route has no program: HTTPD serves the path as a static file. Both can carry a policy, which says who may use the route. This chapter is the full description of the syntax, of the matching and of the policy. The HTTPD User’s Guide shows how to build routes for common cases, in “Routes and Server Modules” and in “Authentication and Security”.

The Syntax of MOD and LOC

LineForm
MODMOD=program [pattern] [AUTH=mode] [RES=class:resource]
LOCLOC=pattern [AUTH=mode] [RES=class:resource]

Table 9. The route lines

PartRules
programThe name of the load module. The first token after the equal sign. HTTPD changes it to upper case and keeps at most 8 characters. A longer name is cut without a message. A module name is at most 8 characters on MVS, so the cut name is the one that HTTPD links to. Required for MOD.
patternThe path that the route claims. See “The Pattern Language”. For MOD it is the second token, and it is optional. If it is left out, HTTPD derives it from the program name (“Routes with an Extension”). For LOC it is the first token and it is required. A token that starts with AUTH=, RES= or RECLAIM=, in any case, is an option and never a pattern.
AUTH=modeThe login that the route needs: NONE, FORM, BASIC or TOKEN, in any case. See “The Login Mode of a Route”.
RES=class:resourceA RACF resource that the user must be allowed to read. See “The Resource Check”.
RECLAIM=xRetired. HTTPD ignores it and writes HTTPD422W. The storage of a module is always released after the request.

Table 10. The parts of a route line

The rules for the line as a whole:

The member may contain these lines:

MOD=MVSMF /zosmf/restconsoles/*   AUTH=TOKEN RES=FACILITY:MVSMF.CONSOLE
MOD=MVSMF /zosmf/*                AUTH=TOKEN
MOD=HTTPDSRV /.dsrv               AUTH=FORM
LOC=/admin/*                      AUTH=BASIC RES=FACILITY:HTTPD.ADMIN
LOC=/*                            AUTH=NONE

The Pattern Language

HTTPD compares the path of the request with the pattern of each route. The path is the part of the URI before the first question mark. HTTPD decodes it first: each %xx becomes the character. A plus sign stays a plus sign (“URL Decoding and Escaping”). The query string never takes part in the match.

A pattern is a wildcard pattern if it contains * or ?. Any other pattern is a plain path.

KindRules
Plain pathThe path of the request and the pattern must be equal. HTTPD ignores the case of the letters. /status matches /STATUS. It does not match /status/.
Wildcard patternHTTPD matches the whole path against the pattern with the library function __patmat. The case of the letters counts. * stands for any characters, also a slash. At the end of the pattern it also stands for no character. Anywhere else it stands for at least one character. ? stands for one character. Any other character stands for itself.

Table 11. The two kinds of pattern

Some examples:

PatternMatchesDoes not match
/zosmf/*/zosmf/info, /zosmf/a/b/zosmf, /ZOSMF/info
/.dsrv/.dsrv, /.DSRV/.dsrv/x
*.rexx/a/b.rexx/a/b.REXX, .rexx
/a*b/axb, /a/b/ab
/doc?.html/doc1.html/doc12.html

Table 12. Examples of patterns

A prefix therefore needs a trailing *. The pattern /admin claims only that one path.

The First Match Wins

HTTPD keeps all routes, MOD and LOC together, in one table in the order of the lines. For a request it walks the table from the top and uses the first route that matches. It never looks for the best match, and it does not test prefixes before extensions. The order of the lines is the only thing that sets the priority.

Two consequences follow.

Write the specific routes first and the general ones last.

A request that no route matches is public. HTTPD serves it as a static file (“Static Routes”).

The Fixed Paths

The paths /login and /logout belong to HTTPD. A request for one of them goes to the login code of HTTPD whatever the routes say and whatever the method is, and no policy applies. The match ignores the case of the letters and needs the whole path. Do not give a route to these two paths.

The paths that match /login.* and /favicon.* are exempt from the policy for the methods GET and HEAD, so that the login page can load its own parts. The case of the letters counts here. The route still applies: HTTPD only skips the check of the login. A MOD route that claims such a path runs its module without a login.

Routes with an Extension

If a MOD line has no pattern, HTTPD builds one from the program name: *. followed by the program name in lower case. The line MOD=REXX becomes the pattern *.rexx.

A pattern that starts with *. makes an extension route, whether HTTPD built it or the line states it. For a request on an extension route HTTPD sets the variable SCRIPT_FILENAME to the document root (DOCROOT, Chapter 1, “The HTTPPRM Keywords”) followed by the path of the request. If HTTPD cannot set the variable, it answers with an internal error (status 500) and does not link to the module. No other route gets this variable from HTTPD.

The pattern is a wildcard pattern, so the match is case sensitive: *.rexx does not match /a/b.REXX. The position in the table counts as for any other route.

The Login Mode of a Route

Each route has one of four modes. The mode says whether the route needs a user, and how HTTPD asks a client that has none.

ModeRules
NONEThe route is public. HTTPD does not check a login. This is also the mode of a route with no AUTH= and no RES=.
FORMThe route needs a login. A client without one gets the HTML login form.
BASICThe route needs a login. A client without one gets status 401 with a Basic challenge.
TOKENThe route needs a login. A client without one gets status 401 and no challenge.

Table 13. The values of AUTH=

The rules for a missing or a wrong mode:

The mode does not choose the way the client proves who it is. Before HTTPD looks for the route, it tries these sources in this order and uses the first one that gives a user.

  1. The cookie Sec-Token, if it holds a valid session token.
  2. The cookie LtpaToken2, if it holds a valid session token.
  3. The header Authorization: Bearer with a session token.
  4. The header Authorization: Basic with user and password.

If neither cookie gives a user, HTTPD reads the Authorization header. A Bearer header ends the search, also when its token is not valid. A header that is neither Bearer nor Basic gives no user.

The login is bound to the address of the client. A user that logged in from another address does not count. Every route accepts every source, whatever its mode. The mode only says what HTTPD sends to a client that has no login.

What Each Mode Answers

HTTPD runs the check in two stages, before it serves a file or links to a module. The first stage asks for a user. The second stage applies only if the route has RES=. It asks RACF whether the user may read the resource. The two stages apply to MOD routes and to LOC routes in the same way, for the methods GET, HEAD, PUT, POST and DELETE.

ModeAnswer
NONENone. HTTPD serves the request.
FORMA GET gets status 302 with Location: /login. The cookie Sec-Uri holds the URI of the request, encoded in base64, and the form sends the user back to it after the login. A POST is read as a login attempt: the fields userid and password of the body are the user. A request with another method gets status 200 and a text that says Invalid REQUEST_METHOD.
BASICStatus 401 with the headers WWW-Authenticate: Basic
realm="name"
, Cache-Control: no-store and Content-Type: text/plain. The body is the line 401
Unauthorized
. The name is the value of REALM (Chapter 1, “The HTTPPRM Keywords”). If the request has the header X-CSRF-ZOSMF-HEADER, whatever its value, HTTPD leaves out the WWW-Authenticate header.
TOKENThe same as BASIC, but HTTPD never sends the WWW-Authenticate header, whatever the request holds.

Table 14. What HTTPD answers to a request without a login

A request with a login, to a route with RES=, goes to the second stage. If the user may not read the resource, HTTPD answers with status 403, the headers Cache-Control: no-store and Content-Type: text/plain, and the body 403 Forbidden. Otherwise HTTPD goes on with the request.

The Resource Check

RuleDetail
FormRES=class:resource. HTTPD splits the value at the first colon. The class is the text before it and the resource is the rest, so the resource may contain colons. HTTPD keeps the case as written.
Access askedREAD.
A wrong valueA value without a colon, and a value with nothing after the colon, give HTTPD412W with the value. HTTPD ignores the option and registers the route. Without an AUTH= the route is then public.
StorageIf HTTPD cannot get storage for the value, it writes HTTPD418E. The route is refused, and the start ends.
Two RES=, one lineThe last well-formed value counts.
With AUTH=NONEHTTPD ignores the resource. See “The Login Mode of a Route”.
Resource without a profileHTTPD counts the return code 4 of the check, “no profile covers the resource”, as permitted, and the return code 0 as well. Every code of 8 and above is a refusal. If the security product answers a resource without a profile with 4, nothing is checked in fact. RAKF answers 4. Whether another product does depends on its settings, not on HTTPD; “What the Server Asks of the Security Product” has the detail. If it answers with 8 or above, the user gets the 403, and HTTPD writes no HTTPD425W. RAKF 2.0.0 (MVS/CE) answers a class that it does not define with 8. A mistake in the resource name, and for RAKF 1.2.x (measured on TK5) a class that is not defined, looks like a missing profile, so HTTPD checks every route with a resource once at the start, and writes HTTPD425W for each one that has no profile. The message names the class, the resource and the pattern of the route. HTTPD does not check routes with the mode NONE. The keyword RES_NOPROFILE=DENY changes the first part: a request to a route whose resource has no profile then gets status 403, and the start writes HTTPD426W instead of HTTPD425W (“Authentication Values”).
DeniedStatus 403.

Table 15. The value of RES=

Limits

LimitValue
Characters read from one line255 (“The Rules of the File”).
Tokens read from one line
  1. HTTPD ignores the rest without a message.
Characters of a program name
  1. HTTPD cuts a longer name without a

message.

Number of routesNo limit from HTTPD. A route that cannot be added for lack of storage gives HTTPD035W or HTTPD423W.
DuplicatesHTTPD does not look for them. The first of two equal routes hides the second.
Reading the memberOnce, at the start. A change of a route needs a new start.

Table 16. Limits of the route lines

Messages While HTTPD Reads the Routes

HTTPD reads the whole member before it decides whether to start. The messages below come while it reads. They go to the console.

MessageStartMeaning
HTTPD036Igoes onA MOD route is registered. The message gives the program and the pattern.
HTTPD417Igoes onA LOC route is registered.
HTTPD410Wgoes onA line used the old name CGI=.
HTTPD421Wsee belowA MOD line without a program name. An option stands where the name belongs, or the value is empty.
HTTPD415Wsee belowA LOC line without a path. An option stands where the path belongs, or the value is empty.
HTTPD411EendsAn AUTH= value that is not valid. HTTPD refuses the route, and writes HTTPD419E.
HTTPD412Wgoes onA RES= value without class and resource. HTTPD ignores it.
HTTPD413Wgoes onA token that is not an option. HTTPD ignores it.
HTTPD414Wgoes onAUTH=NONE with RES=. The resource is not checked.
HTTPD418EendsNo storage for RES=. HTTPD writes HTTPD419E too.
HTTPD422Wgoes onA line used RECLAIM=. HTTPD ignores it.
HTTPD035Wsee belowHTTPD could not register a MOD route. The message gives the program and the pattern.
HTTPD423Wsee belowHTTPD could not register a LOC route.
HTTPD419EendsA route that asked for a login or a resource is lost. The message gives the kind and the pattern.
HTTPD420EendsThe end of the check, see below.

Table 17. Messages of the route lines

A route that asked for a login or for a resource, and that HTTPD cannot build, is never dropped quietly. Without the route the path would be public. HTTPD therefore writes HTTPD419E for each such line. When it has read the whole member, it writes HTTPD420E, ends with CC 0008 and does not open the port. These lines belong to this case:

A route with the mode NONE, and a route without any policy, that HTTPD cannot register give a warning (HTTPD035W, HTTPD423W) and the start goes on. Losing them cannot make the path less protected than the line asked.

Note: HTTPD checks only the words it knows. A line like MOD=ADMIN /admin/* AUTH BASIC has the tokens AUTH and BASIC. HTTPD writes HTTPD413W twice and registers a public route. Look for HTTPD413W in the log after each change of the member. The same holds for RES= with a wrong value and without AUTH=.

Static Routes

A LOC route has no program. The two stages of the check apply as described above. If the request passes, HTTPD serves the path as a static file from the document root (DOCROOT, Chapter 1, “The HTTPPRM Keywords”), for the methods GET, HEAD, PUT, POST and DELETE. A request that no route matches is served the same way, without a check.

A LOC route is the way to protect a part of the document tree. The route does not change what HTTPD serves. It decides who may get it.

Three rules help:

Resident Modules

By default (RESIDENT=YES, Chapter 1, “The HTTPPRM Keywords”) HTTPD loads each module of a MOD route once, after it has read the member and before it serves any request. It holds the copy until the end. A request then finds the module in storage and does not fetch it from the library. HTTPD does this once for each program name, however many routes use the name. The comparison of the names ignores the case.

A module that HTTPD does not hold still works. Each request fetches it, as with RESIDENT=NO. The start shows why in a message.

MessageMeaning
HTTPD440WThe module is not found at the start. HTTPD does not hold it. If it is there later, the route works.
HTTPD441WThe module is not reentrant and not serially reusable. A copy that is held could run twice, so HTTPD does not hold the module.
HTTPD442WThe load failed. The message gives the abend code and the reason code.

Table 18. Messages of the resident modules

A module that HTTPD holds is loaded in the main task of the server. The end of the server releases it. The command D CONFIG shows the number of modules held (Chapter 3, “Console Commands”).

When a Module Does Not Run

If HTTPD cannot run the module of a MOD route, or the module ends badly, HTTPD first releases the storage of the module (a failure of that gives HTTPD906W with the program name and the return code). Then it tells the client and the operator.

For the client, HTTPD sends status 503 with the header Content-Type: text/plain if the module has not sent a status line yet. Then it sends the line External program name text to the body, where text is one of the reasons in the table. For the operator, HTTPD writes HTTPD908E with the program name and the same text.

TextCause
was not foundThe module is in neither the STEPLIB nor the link list (completion code S806).
could not be loadedThe LINK failed with another completion code, for example a fetch error or a storage shortage. A module that returns −1 itself gives the same text.
was dispatched without a program nameThe route has no program name.
could not be started, ESTAE create failedHTTPD could not set up the error exit for the LINK, so the module did not run.
returned rc nThe module ran and ended with the negative return code n. A module that returns a code of zero or more is not a failure.
failed with Sxxx ABENDThe module ended with the system abend xxx.
failed with Unnnn ABENDThe module ended with the user abend nnnn, in decimal. U0801 with the console line @@CRT0 - No storage for C stack means that the region had no block for the stack of the module (“The region”).

Table 19. The reasons for a failure

Only the abends leave a dump to look for. The other reasons mean that the module did not run, or ended by itself.