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
| Line | Form |
MOD | MOD=program [pattern] [AUTH=mode] [RES=class:resource] |
LOC | LOC=pattern [AUTH=mode] [RES=class:resource] |
Table 9. The route lines
| Part | Rules |
| program | The 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. |
| pattern | The 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=mode | The login that the route needs: NONE, FORM, BASIC or TOKEN, in any case. See “The Login Mode of a Route”. |
RES=class:resource | A RACF resource that the user must be allowed to read. See “The Resource Check”. |
RECLAIM=x | Retired. 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 tokens are separated by blanks or tabs. The options may come in any order, but after the pattern.
- HTTPD reads at most 8 tokens. It ignores the rest of the line without a message.
- A token that is not a pattern and not one of the options above gives
HTTPD413Wwith the token, and HTTPD ignores it. The route is still registered. See the warning in “Messages While HTTPD Reads the Routes”. - An option may stand twice. For
AUTH=the last valid mode counts, but a mode that is not valid refuses the route whatever follows. ForRES=the last well-formed value counts. - Each line adds one route. HTTPD does not check for a route that is there already, and it sets no limit on the number of routes.
CGI=is the old name ofMOD=. HTTPD writesHTTPD410Wand reads the line as aMODline.
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.
| Kind | Rules |
| Plain path | The 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 pattern | HTTPD 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:
| Pattern | Matches | Does 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.
- A route with a general pattern hides every route below it that it also matches. The line
LOC=/*beforeMOD=MVSMF /zosmf/*sends every request to theLOCroute. HTTPD gives no message. - The policy of the first route is the whole policy of the request. The policy of a later route that would match too does not apply.
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.
| Mode | Rules |
NONE | The route is public. HTTPD does not check a login. This is also the mode of a route with no AUTH= and no RES=. |
FORM | The route needs a login. A client without one gets the HTML login form. |
BASIC | The route needs a login. A client without one gets status 401 with a Basic challenge. |
TOKEN | The 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:
- A line with
RES=and withoutAUTH=gets the modeBASIC, because a resource check needs a user. - A line with
AUTH=NONEand withRES=givesHTTPD414W. The modeNONEwins, and HTTPD does not check the resource. - A mode that is not one of the four gives
HTTPD411Ewith the first 24 characters of the value. HTTPD refuses the route, and the start ends (“Messages While HTTPD Reads the Routes”).
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.
- The cookie
Sec-Token, if it holds a valid session token. - The cookie
LtpaToken2, if it holds a valid session token. - The header
Authorization: Bearerwith a session token. - The header
Authorization: Basicwith 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.
| Mode | Answer |
NONE | None. HTTPD serves the request. |
FORM | A 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. |
BASIC | Status 401 with the headers WWW-Authenticate: Basic, Cache-Control: no-store and Content-Type: text/plain. The body is the line 401. 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. |
TOKEN | The 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
| Rule | Detail |
| Form | RES=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 asked | READ. |
| A wrong value | A 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. |
| Storage | If HTTPD cannot get storage for the value, it writes HTTPD418E. The route is refused, and the start ends. |
Two RES=, one line | The last well-formed value counts. |
With AUTH=NONE | HTTPD ignores the resource. See “The Login Mode of a Route”. |
| Resource without a profile | HTTPD 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”). |
| Denied | Status 403. |
Table 15. The value of RES=
Limits
| Limit | Value |
| Characters read from one line | 255 (“The Rules of the File”). |
| Tokens read from one line |
|
| Characters of a program name |
message. |
| Number of routes | No limit from HTTPD. A route that cannot be added for lack of storage gives HTTPD035W or HTTPD423W. |
| Duplicates | HTTPD does not look for them. The first of two equal routes hides the second. |
| Reading the member | Once, 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.
| Message | Start | Meaning |
HTTPD036I | goes on | A MOD route is registered. The message gives the program and the pattern. |
HTTPD417I | goes on | A LOC route is registered. |
HTTPD410W | goes on | A line used the old name CGI=. |
HTTPD421W | see below | A MOD line without a program name. An option stands where the name belongs, or the value is empty. |
HTTPD415W | see below | A LOC line without a path. An option stands where the path belongs, or the value is empty. |
HTTPD411E | ends | An AUTH= value that is not valid. HTTPD refuses the route, and writes HTTPD419E. |
HTTPD412W | goes on | A RES= value without class and resource. HTTPD ignores it. |
HTTPD413W | goes on | A token that is not an option. HTTPD ignores it. |
HTTPD414W | goes on | AUTH=NONE with RES=. The resource is not checked. |
HTTPD418E | ends | No storage for RES=. HTTPD writes HTTPD419E too. |
HTTPD422W | goes on | A line used RECLAIM=. HTTPD ignores it. |
HTTPD035W | see below | HTTPD could not register a MOD route. The message gives the program and the pattern. |
HTTPD423W | see below | HTTPD could not register a LOC route. |
HTTPD419E | ends | A route that asked for a login or a resource is lost. The message gives the kind and the pattern. |
HTTPD420E | ends | The 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 line with an
AUTH=value that is not valid. - A line with
RES=for which HTTPD gets no storage. - A line in which an option stands where the program name or the path belongs. An example is
LOC=AUTH=BASIC RES=FACILITY:HTTPD.ADMIN. For such a lineHTTPD419Egives the whole line. HTTPD checks the options to decide: a line that asks for a login or a resource ends the start. A line with onlyAUTH=NONEor without options givesHTTPD421WorHTTPD415Wand the start goes on. - A line for which HTTPD gets no storage to split it into tokens.
- A route with a login or a resource that HTTPD cannot add to the table.
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.
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:
- A route with the mode
NONEand a general pattern, as inLOC=/* AUTH=NONE, claims every path that is not claimed above it. It is a good last line. As a first line it hides all that follows. - A pattern that ends in
/*does not claim the directory itself. Add a plain-path route for it. - The login page lives in the document tree. Its paths
/login.*are exempt (“The Pattern Language”), so aLOC=/* AUTH=FORMroute still lets the page load.
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.
| Message | Meaning |
HTTPD440W | The module is not found at the start. HTTPD does not hold it. If it is there later, the route works. |
HTTPD441W | The module is not reentrant and not serially reusable. A copy that is held could run twice, so HTTPD does not hold the module. |
HTTPD442W | The 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.
| Text | Cause |
was not found | The module is in neither the STEPLIB nor the link list (completion code S806). |
could not be loaded | The 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 name | The route has no program name. |
could not be started, ESTAE create failed | HTTPD could not set up the error exit for the LINK, so the module did not run. |
returned rc n | The 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 ABEND | The module ended with the system abend xxx. |
failed with Unnnn ABEND | The 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.