Except for when it's self-signing certificates, the certmonger daemon
interfaces with CAs by calling out to enrollment helpers.  The helpers pass
signing requests to CAs, pass issued certificates back, and if they support it,
are used to fetch other data about CAs as well.

The certmonger daemon passes information to helpers through the environment
when it runs them.  The helpers communicate results back to the daemon using a
combination of exit status and data provided on standard output.

When certmonger runs a helper, the $CERTMONGER_OPERATION is used to indicate
what the daemon expects the helper to do.  If a helper does not implement an
operation specified in the $CERTMONGER_OPERATION variable, it should exit with
status 6 to indicate that the operation is not supported.

I recommend that helpers be written so that they can be run interactively for
troubleshooting purposes.  The most frequent case is enrollment, so when
$CERTMONGER_OPERATION is not set, it's suggested that "SUBMIT" is assumed.

For testing purposes, a helper can be added by creating a file in the CAs
directory (usually /var/lib/certmonger/cas) with these contents:

  id=Test
  ca_type=EXTERNAL
  ca_is_default=0
  ca_external_helper=/usr/libexec/certmonger/my-helper

Equivalently, this can be accomplished by running:

  getcert add-ca -c Test -e "/usr/libexec/certmonger/my-helper"

Passing the "-c Test" flag to the "getcert request", "getcert resubmit", or
"getcert rekey" commands will then cause the daemon to use your helper to
attempt the appropriate operation.  Note that the command can include options,
but things are less likely to break if the helper has sensible defaults.

The following sections describe what should happen for each operation when a
helper is called.

* "SUBMIT"

  This is called the first time the daemon attempts to send an enrollment
  request to a CA.  The signing data, in PEM form, is provided in the
  environment.  Some of the data from the request is also broken out and
  provided in the environment:

  * CERTMONGER_REQ_SUBJECT
    The subject name from the request, in text form.
  * CERTMONGER_REQ_EMAIL
    Any rfc822Name subject alt name values from the request.
  * CERTMONGER_REQ_HOSTNAME
    Any dNSName subject alt name values from the request.
  * CERTMONGER_REQ_PRINCIPAL
    Any Kerberos principal name subject alt name values from the request.
  * CERTMONGER_CA_PROFILE
    The name of the enrollment profile/template/certtype to use, if one
    was specified.
  * CERTMONGER_CSR
    The actual enrollment request, PKCS#10 format, PEM-encoded.
  * CERTMONGER_CERTIFICATE
    An older certificate, if we were previously issued one.

  If multiple options were passed in for CERTMONGER_REQ_EMAIL,
  CERTMONGER_REQ_HOSTNAME, CERTMONGER_REQ_PRINCIPAL or
  CERTMONGER_REQ_IP_ADDRESS (0.78+) then the variable will include all
  values passed in delimited by \n.

  These are also present starting with version 0.73:

  * CERTMONGER_CA_NICKNAME
    The name by which the CA is known, and would have been specified to the -c
    option to the "getcert" command.  If your helper is called in multiple CA
    configurations, you may want to use this value to distinguish between them
    in order to provide different behavior.
  * CERTMONGER_SPKAC
    The signing request as a signed public key and challenge (SPKAC).
  * CERTMONGER_SPKI
    The subjectPublicKeyInfo field from the signing request.
  * CERTMONGER_KEY_TYPE
    The type of key included in the signing request.

  These may also be present starting with version 0.77, though you probably
  won't use them:

  * CERTMONGER_SCEP_CA_IDENTIFIER
    An identifier to pass to an SCEP server when requesting its capabilities
    list or copies of it and its CA's certificate.
  * CERTMONGER_PKCSREQ
    An SCEP PKCSReq pkiMessage.  If the daemon is attempting to change keys,
    this will be signed with the old key.
  * CERTMONGER_PKCSREQ_REKEY
    An SCEP PKCSReq pkiMessage.  If the daemon is attempting to change keys,
    this will be signed with the new key, otherwise it is not set.
  * CERTMONGER_GETCERTINITIAL
    An SCEP GetCertInitial pkiMessage.  If the daemon is attempting to change
    keys, this will be signed with the old key.
  * CERTMONGER_GETCERTINITIAL_REKEY
    An SCEP GetCertInitial pkiMessage.  If the daemon is attempting to change
    keys, this will be signed with the new key, otherwise it is not set.
  * CERTMONGER_SCEP_RA_CERTIFICATE
    The SCEP server's RA certificate.
  * CERTMONGER_SCEP_CA_CERTIFICATE
    The SCEP server's CA certificate.
  * CERTMONGER_SCEP_CERTIFICATES
    Additional certificates in the SCEP server's certifying chain.

  These are also present starting with version 0.78:

  * CERTMONGER_REQ_IP_ADDRESS
    Any iPAddress subject alt name values from the request.

  These are also present starting with version 0.79:

  * CERTMONGER_CA_ISSUER
    The requested issuer for enrollment.

  The helper is expected to use this information, along with whatever
  credentials it has or is passed on the command line, to send the signing
  request to the CA.

  * If a certificate is issued, output it in PEM form and exit with status 0.
    See footnote 1 for information about formatting the result.
  * If the client should wait for a period of time, output a "cookie" value and
    exit with status 1.  The daemon will try again later at a time of its
    choosing (the default is currently 7 days).
  * If the request was rejected outright, output an error message, and exit
    with status 2.
  * If there was an error connecting to the server, output an error message and
    exit with status 3.  The daemon will try again later.
  * If the helper requires additional configuration data, output an error
    message and exit with status 4.
  * If the client should wait for a specific period of time (for example, if
    the CA has told it when to try again), output a delay size in seconds, a
    newline, and a "cookie" value, and exit with status 5.  The daemon will try
    again after the specified amount of time has passed.
  * If the helper needs SCEP data, exit with status 16.  Your helper probably
    won't need to do this.
  * If the CA indicates that the client needs to try again using a different
    key pair in the signing request (for example, if its policy limits the
    number of times a particular key pair can be enrolled, or the length of
    time one can be in service), exit with status 17.  The daemon will generate
    a new key pair and try again.
  * If the helper does not understand what is being asked of it, exit with
    status 6.  You should never return this value for "SUBMIT" or "POLL", but
    it is mentioned here so that we can refer to this list later.

* "POLL"

  If the helper previously returned with status 1 or 5, this is the daemon
  trying again.  The same information supplied for "SUBMIT" requests will be
  provided in the environment.  Additionally, the "CERTMONGER_CA_COOKIE"
  variable will hold the cookie value returned by the previous call to the
  helper.  If your process requires multiple steps, the cookie is suitable for
  keeping track of which step is next.
  If your helper never returns status 1 or 5, this will not be used, and you
  need not implement logic for it.
  Report results as you would for the "SUBMIT" operation.

* "IDENTIFY":

  Output version information for your helper, and exit with status 0.  This
  information is tracked by the daemon and included in the output of the
  "getcert list-cas -v" command.  Optional.

* "GET-NEW-REQUEST-REQUIREMENTS"

  Output a list of environment variable names which are expected to have
  non-empty values when the helper is run in SUBMIT or POLL mode.  The list can
  be either comma- or newline-separated.
  At some point, we'll teach getcert to instruct people to supply values that
  are required by the CA that they intend to use if it finds that they didn't
  supply one of these.
  Support for this operation is optional.

* "GET-RENEW-REQUEST-REQUIREMENTS"

  Just like "GET-NEW-REQUEST-REQUIREMENTS", except for cases when the client
  attempts to renew an already-issued certificate.  In most cases, your helper
  will want to do the same thing for "GET-RENEW-REQUEST-REQUIREMENTS" as it
  does for "GET-NEW-REQUEST-REQUIREMENTS"
  Support for this operation is optional.

* "GET-SUPPORTED-TEMPLATES"

  Output a list of supported profile/template/certtype names offered and
  recognized by the CA.  The list can be either comma- or newline-separated.
  At some point, we'll teach getcert to validate values it receives for its -T
  option against this list.
  Support for this operation is optional.

* "GET-DEFAULT-TEMPLATE"

  Output a single supported profile/template/certtype name offered and
  recognized by the CA.  If there is no default, output nothing.
  At some point, we'll teach getcert to use this value as a default if it is
  not passed the -T option.
  Support for this operation is optional.

* "FETCH-SCEP-CA-CAPS"

  If your helper uses SCEP, connect to the server, issue a GetCACaps request,
  and output the results.  Most helpers are not expected to implement this, and
  should exit with status 6 to indicate that they don't support it.

* "FETCH-SCEP-CA-CERTS"

  If your helper uses SCEP, connect to the server, issue a GetCACert and
  GetCAChain requests, and output the results.  Most helpers are not expected
  to implement these, and should exit with status 6 to indicate that they don't
  support it.

* "FETCH-ROOTS"

  If the helper has a way to read the CA's root certificate over an
  authenticated and integrity-protected channel, output a suggested nickname,
  the certificate in PEM format.  If there are other trusted certificates,
  follow that with a blank line and one or more nickname/certificate sequences.
  If there are other certificates which the client might need (for example,
  others in the certifying chain), repeat for those.  Note that if there are
  chain certificates but no supplemental root certificates, the root
  certificate should be followed by two blank lines.
  Support for this operation is optional.  If you can not guarantee that the
  data produced is authenticated and has not been tampered with, do not
  implement this.
  The format described here is recognized to be error-prone and will be
  replaced with a JSON object in the future.

* (not set)

  To ease troubleshooting, my suggestion is to treat the CERTMONGER_OPERATION
  not being set as if it was set to SUBMIT, or POLL if a cookie value is passed
  to your helper via a command-line option.

* Anything else.

  For future-proofing, exit with status 6.

Footnotes:

1. When a certificate is issued, it can be output as a PEM-formatted X.509
   certificate, a PEM-formatted "certificates only" PKCS#7 signed-data item, a
   PEM-formatted PKCS#7 enveloped-data with encapsulated content identified as
   type "data", containing either an X.509 certificate or PKCS#7 signed-data
   item in binary form, or any of the previous in binary form.  When PEM
   formatting is used, multiple PEM-formatted items can be output if needed.
