| /* Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef mod_md_md_acme_h |
| #define mod_md_md_acme_h |
| |
| struct apr_array_header_t; |
| struct apr_bucket_brigade; |
| struct md_http_response_t; |
| struct apr_hash_t; |
| struct md_http_t; |
| struct md_json_t; |
| struct md_pkey_t; |
| struct md_t; |
| struct md_acme_acct_t; |
| struct md_acmev2_acct_t; |
| struct md_store_t; |
| struct md_result_t; |
| |
| #define MD_PROTO_ACME "ACME" |
| |
| #define MD_AUTHZ_CHA_HTTP_01 "http-01" |
| #define MD_AUTHZ_CHA_SNI_01 "tls-sni-01" |
| |
| #define MD_ACME_VERSION_UNKNOWN 0x0 |
| #define MD_ACME_VERSION_1 0x010000 |
| #define MD_ACME_VERSION_2 0x020000 |
| |
| #define MD_ACME_VERSION_MAJOR(i) (((i)&0xFF0000) >> 16) |
| |
| typedef enum { |
| MD_ACME_S_UNKNOWN, /* MD has not been analysed yet */ |
| MD_ACME_S_REGISTERED, /* MD is registered at CA, but not more */ |
| MD_ACME_S_TOS_ACCEPTED, /* Terms of Service were accepted by account holder */ |
| MD_ACME_S_CHALLENGED, /* MD challenge information for all domains is known */ |
| MD_ACME_S_VALIDATED, /* MD domains have been validated */ |
| MD_ACME_S_CERTIFIED, /* MD has valid certificate */ |
| MD_ACME_S_DENIED, /* MD domains (at least one) have been denied by CA */ |
| } md_acme_state_t; |
| |
| typedef struct md_acme_t md_acme_t; |
| |
| typedef struct md_acme_req_t md_acme_req_t; |
| /** |
| * Request callback on a successful HTTP response (status 2xx). |
| */ |
| typedef apr_status_t md_acme_req_res_cb(md_acme_t *acme, |
| const struct md_http_response_t *res, void *baton); |
| |
| /** |
| * Request callback to initialize before sending. May be invoked more than once in |
| * case of retries. |
| */ |
| typedef apr_status_t md_acme_req_init_cb(md_acme_req_t *req, void *baton); |
| |
| /** |
| * Request callback on a successful response (HTTP response code 2xx) and content |
| * type matching application/.*json. |
| */ |
| typedef apr_status_t md_acme_req_json_cb(md_acme_t *acme, apr_pool_t *p, |
| const apr_table_t *headers, |
| struct md_json_t *jbody, void *baton); |
| |
| /** |
| * Request callback on detected errors. |
| */ |
| typedef apr_status_t md_acme_req_err_cb(md_acme_req_t *req, |
| const struct md_result_t *result, void *baton); |
| |
| |
| typedef apr_status_t md_acme_new_nonce_fn(md_acme_t *acme); |
| typedef apr_status_t md_acme_req_init_fn(md_acme_req_t *req, struct md_json_t *jpayload); |
| |
| typedef apr_status_t md_acme_post_fn(md_acme_t *acme, |
| md_acme_req_init_cb *on_init, |
| md_acme_req_json_cb *on_json, |
| md_acme_req_res_cb *on_res, |
| md_acme_req_err_cb *on_err, |
| void *baton); |
| |
| struct md_acme_t { |
| const char *url; /* directory url of the ACME service */ |
| const char *sname; /* short name for the service, not necessarily unique */ |
| apr_pool_t *p; |
| const char *user_agent; |
| const char *proxy_url; |
| |
| const char *acct_id; /* local storage id account was loaded from or NULL */ |
| struct md_acme_acct_t *acct; /* account at ACME server to use for requests */ |
| struct md_pkey_t *acct_key; /* private RSA key belonging to account */ |
| |
| int version; /* as detected from the server */ |
| union { |
| struct { |
| const char *new_authz; |
| const char *new_cert; |
| const char *new_reg; |
| const char *revoke_cert; |
| |
| } v1; |
| struct { |
| const char *new_account; |
| const char *new_order; |
| const char *key_change; |
| const char *revoke_cert; |
| const char *new_nonce; |
| } v2; |
| } api; |
| const char *ca_agreement; |
| const char *acct_name; |
| |
| md_acme_new_nonce_fn *new_nonce_fn; |
| md_acme_req_init_fn *req_init_fn; |
| md_acme_post_fn *post_new_account_fn; |
| |
| struct md_http_t *http; |
| |
| const char *nonce; |
| int max_retries; |
| struct md_result_t *last; /* result of last request */ |
| }; |
| |
| /** |
| * Global init, call once at start up. |
| */ |
| apr_status_t md_acme_init(apr_pool_t *pool, const char *base_version, int init_ssl); |
| |
| /** |
| * Create a new ACME server instance. If path is not NULL, will use that directory |
| * for persisting information. Will load any information persisted in earlier session. |
| * url needs only be specified for instances where this has never been persisted before. |
| * |
| * @param pacme will hold the ACME server instance on success |
| * @param p pool to used |
| * @param url url of the server, optional if known at path |
| * @param proxy_url optional url of a HTTP(S) proxy to use |
| */ |
| apr_status_t md_acme_create(md_acme_t **pacme, apr_pool_t *p, const char *url, |
| const char *proxy_url); |
| |
| /** |
| * Contact the ACME server and retrieve its directory information. |
| * |
| * @param acme the ACME server to contact |
| */ |
| apr_status_t md_acme_setup(md_acme_t *acme, struct md_result_t *result); |
| |
| void md_acme_report_result(md_acme_t *acme, apr_status_t rv, struct md_result_t *result); |
| |
| /**************************************************************************************************/ |
| /* account handling */ |
| |
| /** |
| * Clear any existing account data from acme instance. |
| */ |
| void md_acme_clear_acct(md_acme_t *acme); |
| |
| apr_status_t md_acme_POST_new_account(md_acme_t *acme, |
| md_acme_req_init_cb *on_init, |
| md_acme_req_json_cb *on_json, |
| md_acme_req_res_cb *on_res, |
| md_acme_req_err_cb *on_err, |
| void *baton); |
| |
| /** |
| * Get the local name of the account currently used by the acme instance. |
| * Will be NULL if no account has been setup successfully. |
| */ |
| const char *md_acme_acct_id_get(md_acme_t *acme); |
| const char *md_acme_acct_url_get(md_acme_t *acme); |
| |
| /** |
| * Specify the account to use by name in local store. On success, the account |
| * the "current" one used by the acme instance. |
| */ |
| apr_status_t md_acme_use_acct(md_acme_t *acme, struct md_store_t *store, |
| apr_pool_t *p, const char *acct_id); |
| |
| /** |
| * Get the local name of the account currently used by the acme instance. |
| * Will be NULL if no account has been setup successfully. |
| */ |
| const char *md_acme_acct_id_get(md_acme_t *acme); |
| |
| /** |
| * Agree to the given Terms-of-Service url for the current account. |
| */ |
| apr_status_t md_acme_agree(md_acme_t *acme, apr_pool_t *p, const char *tos); |
| |
| /** |
| * Confirm with the server that the current account agrees to the Terms-of-Service |
| * given in the agreement url. |
| * If the known agreement is equal to this, nothing is done. |
| * If it differs, the account is re-validated in the hope that the server |
| * announces the Tos URL it wants. If this is equal to the agreement specified, |
| * the server is notified of this. If the server requires a ToS that the account |
| * thinks it has already given, it is resend. |
| * |
| * If an agreement is required, different from the current one, APR_INCOMPLETE is |
| * returned and the agreement url is returned in the parameter. |
| */ |
| apr_status_t md_acme_check_agreement(md_acme_t *acme, apr_pool_t *p, |
| const char *agreement, const char **prequired); |
| |
| apr_status_t md_acme_save_acct(md_acme_t *acme, apr_pool_t *p, struct md_store_t *store); |
| |
| /** |
| * Deactivate the current account at the ACME server.. |
| */ |
| apr_status_t md_acme_acct_deactivate(md_acme_t *acme, apr_pool_t *p); |
| |
| /**************************************************************************************************/ |
| /* request handling */ |
| |
| struct md_acme_req_t { |
| md_acme_t *acme; /* the ACME server to talk to */ |
| apr_pool_t *p; /* pool for the request duration */ |
| |
| const char *url; /* url to POST the request to */ |
| const char *method; /* HTTP method to use */ |
| apr_table_t *prot_hdrs; /* JWS headers needing protection (nonce) */ |
| struct md_json_t *req_json; /* JSON to be POSTed in request body */ |
| |
| apr_table_t *resp_hdrs; /* HTTP response headers */ |
| struct md_json_t *resp_json; /* JSON response body received */ |
| |
| apr_status_t rv; /* status of request */ |
| |
| md_acme_req_init_cb *on_init; /* callback to initialize the request before submit */ |
| md_acme_req_json_cb *on_json; /* callback on successful JSON response */ |
| md_acme_req_res_cb *on_res; /* callback on generic HTTP response */ |
| md_acme_req_err_cb *on_err; /* callback on encountered error */ |
| int max_retries; /* how often this might be retried */ |
| void *baton; /* userdata for callbacks */ |
| struct md_result_t *result; /* result of this request */ |
| }; |
| |
| apr_status_t md_acme_req_body_init(md_acme_req_t *req, struct md_json_t *payload); |
| |
| apr_status_t md_acme_GET(md_acme_t *acme, const char *url, |
| md_acme_req_init_cb *on_init, |
| md_acme_req_json_cb *on_json, |
| md_acme_req_res_cb *on_res, |
| md_acme_req_err_cb *on_err, |
| void *baton); |
| /** |
| * Perform a POST against the ACME url. If a on_json callback is given and |
| * the HTTP response is JSON, only this callback is invoked. Otherwise, on HTTP status |
| * 2xx, the on_res callback is invoked. If no on_res is given, it is considered a |
| * response error, since only JSON was expected. |
| * At least one callback needs to be non-NULL. |
| * |
| * @param acme the ACME server to talk to |
| * @param url the url to send the request to |
| * @param on_init callback to initialize the request data |
| * @param on_json callback on successful JSON response |
| * @param on_res callback on successful HTTP response |
| * @param baton userdata for callbacks |
| */ |
| apr_status_t md_acme_POST(md_acme_t *acme, const char *url, |
| md_acme_req_init_cb *on_init, |
| md_acme_req_json_cb *on_json, |
| md_acme_req_res_cb *on_res, |
| md_acme_req_err_cb *on_err, |
| void *baton); |
| |
| /** |
| * Retrieve a JSON resource from the ACME server |
| */ |
| apr_status_t md_acme_get_json(struct md_json_t **pjson, md_acme_t *acme, |
| const char *url, apr_pool_t *p); |
| |
| |
| apr_status_t md_acme_req_body_init(md_acme_req_t *req, struct md_json_t *jpayload); |
| |
| apr_status_t md_acme_protos_add(struct apr_hash_t *protos, apr_pool_t *p); |
| |
| #endif /* md_acme_h */ |