From c7afb5baec2e857ae11771dddb4d98a5144eb644 Mon Sep 17 00:00:00 2001 From: Ingo Franzki Date: Mon, 11 May 2020 14:48:41 +0200 Subject: [PATCH] libekmfweb: Retrieve information about keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Retrieve information about keys managed by EKMF Web, such as the key algorithm, the key size and type. Additional, the key's tags are retrieved. Label tags are used to build the label name of a key. Custom tags can be used to store any kind of textual data together with a key. Signed-off-by: Ingo Franzki Signed-off-by: Jan Höppner --- include/ekmfweb/ekmfweb.h | 182 +++++++++++ libekmfweb/ekmfweb.c | 660 ++++++++++++++++++++++++++++++++++++++ libekmfweb/libekmfweb.map | 4 + libekmfweb/utilities.c | 454 ++++++++++++++++++++++++++ libekmfweb/utilities.h | 21 ++ 5 files changed, 1321 insertions(+) diff --git a/include/ekmfweb/ekmfweb.h b/include/ekmfweb/ekmfweb.h index 032f4a27..6eb9050e 100644 --- a/include/ekmfweb/ekmfweb.h +++ b/include/ekmfweb/ekmfweb.h @@ -466,6 +466,73 @@ struct ekmf_template_info { const char *updated_on; }; +struct ekmf_tag { + /** name of the tag */ + const char *name; + /** value of the tag */ + const char *value; +}; + +struct ekmf_tag_list { + /** array of tags */ + struct ekmf_tag *tags; + /** number of tags in array above */ + size_t num_tags; +}; + +struct ekmf_exporting_key { + /** name of the exporting key */ + const char *name; + /** uuid of the exporting key */ + const char *uuid; +}; + +struct ekmf_export_control { + /** If true, export is allowed using the exporting keys below */ + bool export_allowed; + /** array of erporting keys */ + struct ekmf_exporting_key *exporting_keys; + /** number of keys in array above */ + size_t num_exporting_keys; +}; + +struct ekmf_key_info { + /** label (name) of the key */ + const char *label; + /** Optional: description of the key (can be NULL) */ + const char *description; + /** UUID of the key */ + const char *uuid; + /** type of the key, e.g. CIPHER */ + const char *key_type; + /** algorithm of the key, e.g. AES */ + const char *algorithm; + /** bit size of the key */ + size_t key_size; + /** state of the key, e.g. ACTIVE */ + const char *state; + /** type of the keystore, e.g. PERVASIVE_ENCRYPTION */ + const char *keystore_type; + /** name of the template used to generate the key */ + const char *template; + /** UUID of the template used to generate the key */ + const char *template_uuid; + /** label tag list */ + struct ekmf_tag_list label_tags; + /** custom tag list */ + struct ekmf_tag_list custom_tags; + /** export control information */ + struct ekmf_export_control export_control; + /** timestamp when the key was activated */ + const char *activate_on; + /** timestamp when the key expires */ + const char *expires_on; + /** timestamp when the key was created */ + const char *created_on; + /** timestamp when the key was updated */ + const char *updated_on; +}; + /** * Callback function used with the ekmf_list_templates function. This * callback is called for each template found. @@ -612,6 +679,121 @@ int ekmf_clone_template_info(const struct ekmf_template_info *src, */ void ekmf_free_template_info(struct ekmf_template_info *template); +/** + * Callback function used with the ekmf_list_keys function. This + * callback is called for each ky found. + * + * @param curl_handle a CURL handle that can be used to perform further + * EKMFWeb functions within the callback. + * @param template_info a struct containing information about the key. + * If any of the information needs to be kept, then the + * callback function must make a copy of the + * information. The memory holding the information + * passed to the callback is no longer valid after the + * callback has returned. + * @param private the private pointer that was specified with the + * ekmf_list_keys invocation. + * + * @returns zero for success, a negative errno in case of an error. + * When a nonzero return code is returned, the key listing process stops, + * and ekmf_list_keys returns the return code from the callback. + */ +typedef int (*ekmf_key_cb_t)(CURL *curl_handle, + struct ekmf_key_info *key_info, + void *private); + +/** + * List available keys. The keys are ordered by name in ascending order. + * + * To perform a single request, set curl_handle to NULL. This will cause the + * function to initialize a new CURL handle, use it, and destroy it. + * If you plan to perform multiple requests to the same host, supply the address + * of a CURL pointer that is initially NULL. This function will then initialize + * a new CURL handle on the first call. On subsequent calls, pass in the address + * of the same CURL pointer so that the CURL handle is reused. After the last + * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). + * + * @param config the configuration structure + * @param curl_handle address of a CURL handle used for reusing the same + * CURL handle with multiple requests. + * @param key_cb a callback function that is called for each key + * found + * @param private a pointer that is passed as-is to the callback + * @param name_pattern a pattern to filter by name, or NULL to list all. + * @param states the states of the keys to list, or NULL to list keys + * in ACTIVE state only. Multiple states can be + * specified separated by comma.* + * @param tags a list of custom tags to use as filter, or NULL + * @param error_msg on return: If not NULL, then a textual error message + * is returned in case of a failing request. The caller + * must free the error string when it is not NULL. + * @param verbose if true, verbose messages are printed + * + * @returns zero for success, a negative errno in case of an error. + * -EACCES is returned, if no or no valid login token is available. + * -EPERM is returned if the login token does not have permission to + * list the keys + */ +int ekmf_list_keys(const struct ekmf_config *config, CURL **curl_handle, + ekmf_key_cb_t key_cb, void *private, + const char *name_pattern, const char *states, + const struct ekmf_tag_list *tags, + char **error_msg, bool verbose); + +/** + * Get information about a key by its UUID. + * + * To perform a single request, set curl_handle to NULL. This will cause the + * function to initialize a new CURL handle, use it, and destroy it. + * If you plan to perform multiple requests to the same host, supply the address + * of a CURL pointer that is initially NULL. This function will then initialize + * a new CURL handle on the first call. On subsequent calls, pass in the address + * of the same CURL pointer so that the CURL handle is reused. After the last + * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). + * + * @param config the configuration structure + * @param curl_handle address of a CURL handle used for reusing the same + * CURL handle with multiple requests. + * @param key_uuid the UUID of the key to get info for + * @param key an address of a key info pointer. On return + * the pointer is updated to point to a newly allocated + * key info struct. It must be freed by the caller + * using ekmf_free_key_info when no longer needed. + * @param error_msg on return: If not NULL, then a textual error message + * is returned in case of a failing request. The caller + * must free the error string when it is not NULL. + * @param verbose if true, verbose messages are printed + * + * @returns zero for success, a negative errno in case of an error. + * -EACCES is returned, if no or no valid login token is available. + * -EPERM is returned if the login token does not have permission to + * get the key info + */ +int ekmf_get_key_info(const struct ekmf_config *config, CURL **curl_handle, + const char *key_uuid, struct ekmf_key_info **key, + char **error_msg, bool verbose); + +/** + * Clones a key info structure by making a deep copy of all strings and + * arrays. + * The copied key info must be freed using ekmf_free_key_info() by + * the caller. + * + * @param src the source key info structure + * @param dest the destination key info structure + * + * @returns zero for success, a negative errno in case of an error + */ +int ekmf_clone_key_info(const struct ekmf_key_info *src, + struct ekmf_key_info **dest); + +/** + * Free a key info structure. + * + * @param key the key info to free + */ +void ekmf_free_key_info(struct ekmf_key_info *key); + /** * Close the connection to the EKMFWeb server by destroying the CURL handle. * diff --git a/libekmfweb/ekmfweb.c b/libekmfweb/ekmfweb.c index 237efea3..ac4072cb 100644 --- a/libekmfweb/ekmfweb.c +++ b/libekmfweb/ekmfweb.c @@ -44,6 +44,15 @@ #define EKMF_URI_SYSTEM_PUBKEY "/api/v1/system/publicKey" #define EKMF_URI_KEYS_EXPORT "/api/v1/keys/%s/export" +#define EKMF_URI_KEYS_TAGS "/api/v1/keys/%s/tags" +#define EKMF_URI_KEYS_EXPORT_CONTROL "/api/v1/keys/%s/exportControl" +#define EKMF_URI_KEYS_GET "/api/v1/keys/%s" +#define EKMF_URI_KEYS_LIST "/api/v1/keys" \ + "?state=%s" \ + "&orderBy=%s" \ + "&namePattern=%s" \ + "&tags=%s" +#define EKMF_URI_KEYS_LIST_STATE "&state=" #define EKMF_URI_TEMPLATE_GET "/api/v1/templates/%s" #define EKMF_URI_TEMPLATE_LIST "/api/v1/templates" \ "?templateStates=%s" \ @@ -53,9 +62,11 @@ #define LIST_ELEMENTS_PER_PAGE 20 #define TEMPLATE_STATE_ACTIVE "ACTIVE" +#define KEY_STATE_ACTIVE "ACTIVE" #define KEY_ALGORITHM_AES "AES" #define KEYSTORE_TYPE_PERV_ENCR "PERVASIVE_ENCRYPTION" #define ORDER_BY_NAME_ASC "name%3Aasc" +#define ORDER_BY_LABEL_ASC "label%3Aasc" #define pr_verbose(verbose, fmt...) do { \ if (verbose) \ @@ -2725,6 +2736,655 @@ void ekmf_free_template_info(struct ekmf_template_info *template) free(template); } +/** + * Gets the custom tags of a key by key-uuid. The custom tags are returned as + * JSON array. The returned JSON array must be freed by the caller using + * json_object_put(). + */ +static int _ekmf_get_custom_tags(const struct ekmf_config *config, + const char *key_uuid, CURL *curl, + json_object **custom_tags, + const char *login_token, char **error_msg, + bool verbose) +{ + json_object *response_obj = NULL; + char *escaped_uuid = NULL; + char *uri = NULL; + long status_code; + int rc; + + if (config == NULL || key_uuid == NULL || custom_tags == NULL || + curl == NULL) + return -EINVAL; + + escaped_uuid = curl_easy_escape(curl, key_uuid, 0); + if (escaped_uuid == NULL) { + pr_verbose(verbose, "Failed to url-escape the key uuid"); + rc = -EIO; + goto out; + } + + if (asprintf(&uri, EKMF_URI_KEYS_TAGS, escaped_uuid) < 0) { + pr_verbose(verbose, "asprintf failed"); + rc = -ENOMEM; + goto out; + } + + rc = _ekmf_perform_request(config, uri, "GET", NULL, NULL, login_token, + &response_obj, NULL, &status_code, error_msg, + curl, verbose); + if (rc != 0) { + pr_verbose(verbose, "Failed perform the REST call"); + if (rc > 0) + rc = -EIO; + goto out; + } + + switch (status_code) { + case 200: + break; + case 400: + pr_verbose(verbose, "Bad request"); + rc = -EBADMSG; + goto out; + case 401: + pr_verbose(verbose, "Not authorized"); + rc = -EACCES; + goto out; + case 403: + pr_verbose(verbose, "Insufficient permissions"); + rc = -EPERM; + goto out; + default: + pr_verbose(verbose, "REST Call failed with HTTP status code: " + "%ld", status_code); + rc = -EIO; + goto out; + } + + JSON_CHECK_OBJ(response_obj, json_type_array, rc, -EIO, + "No or invalid response content", verbose, out); + + *custom_tags = response_obj; + rc = 0; + +out: + if (uri != NULL) + free(uri); + if (escaped_uuid != NULL) + curl_free(escaped_uuid); + if (rc != 0 && response_obj != NULL) + json_object_put(response_obj); + + return rc; +} + +/** + * Gets the export control infos of a key by key-uuid. The export control info + * is returned as JSON object. The returned JSON object must be freed by the + * caller using json_object_put(). + */ +static int _ekmf_get_export_control(const struct ekmf_config *config, + const char *key_uuid, CURL *curl, + json_object **export_control, + const char *login_token, char **error_msg, + bool verbose) +{ + json_object *response_obj = NULL; + char *escaped_uuid = NULL; + char *uri = NULL; + long status_code; + int rc; + + if (config == NULL || key_uuid == NULL || export_control == NULL || + curl == NULL) + return -EINVAL; + + escaped_uuid = curl_easy_escape(curl, key_uuid, 0); + if (escaped_uuid == NULL) { + pr_verbose(verbose, "Failed to url-escape the key uuid"); + rc = -EIO; + goto out; + } + + if (asprintf(&uri, EKMF_URI_KEYS_EXPORT_CONTROL, escaped_uuid) < 0) { + pr_verbose(verbose, "asprintf failed"); + rc = -ENOMEM; + goto out; + } + + rc = _ekmf_perform_request(config, uri, "GET", NULL, NULL, login_token, + &response_obj, NULL, &status_code, error_msg, + curl, verbose); + if (rc != 0) { + pr_verbose(verbose, "Failed perform the REST call"); + if (rc > 0) + rc = -EIO; + goto out; + } + + switch (status_code) { + case 200: + break; + case 400: + pr_verbose(verbose, "Bad request"); + rc = -EBADMSG; + goto out; + case 401: + pr_verbose(verbose, "Not authorized"); + rc = -EACCES; + goto out; + case 403: + pr_verbose(verbose, "Insufficient permissions"); + rc = -EPERM; + goto out; + default: + pr_verbose(verbose, "REST Call failed with HTTP status code: " + "%ld", status_code); + rc = -EIO; + goto out; + } + + JSON_CHECK_OBJ(response_obj, json_type_object, rc, -EIO, + "No or invalid response content", verbose, out); + + *export_control = response_obj; + rc = 0; + +out: + if (uri != NULL) + free(uri); + if (escaped_uuid != NULL) + curl_free(escaped_uuid); + if (rc != 0 && response_obj != NULL) + json_object_put(response_obj); + + return rc; +} + +/** + * Get the custom tags for a key and build the key info structure + */ +static int _ekmf_build_key_info(const struct ekmf_config *config, CURL *curl, + const char *login_token, json_object *obj, + struct ekmf_key_info *key, bool copy, + char **error_msg, bool verbose) +{ + json_object *export_control = NULL; + json_object *custom_tags = NULL; + int rc; + + rc = _ekmf_get_custom_tags(config, json_get_string(obj, "keyId"), + curl, &custom_tags, login_token, error_msg, + verbose); + if (rc != 0) { + pr_verbose(verbose, "Failed to get the custom tags for key %s", + json_get_string(obj, "keyId")); + goto out; + } + + rc = _ekmf_get_export_control(config, json_get_string(obj, "keyId"), + curl, &export_control, login_token, + error_msg, verbose); + if (rc != 0) { + pr_verbose(verbose, "Failed to get the custom tags for key %s", + json_get_string(obj, "keyId")); + goto out; + } + + rc = json_build_key_info(obj, custom_tags, export_control, key, copy); + if (rc != 0) { + pr_verbose(verbose, "Failed to build key info"); + goto out; + } + +out: + /* + * Add custom tags and export control JSON objects to the key object, + * so that these objects are also owned by the key object, and thus are + * freed together with it, when the caller puts/frees the key object. + */ + if (custom_tags != NULL) + json_object_object_add_ex(obj, "_custom_tags_", custom_tags, 0); + if (export_control != NULL) + json_object_object_add_ex(obj, "_export_control_", + export_control, 0); + + return rc; +} + + +struct ekmf_key_cb_data_t { + const struct ekmf_config *config; + const char *login_token; + char **error_msg; + ekmf_key_cb_t key_cb; + void *cb_private; +}; + +/** + * Callback for key list function. Builds the key info structure + * and calls the application callback. + */ +static int _ekmf_key_cb(CURL *curl, json_object *element, + void *private, bool verbose) +{ + struct ekmf_key_cb_data_t *cb_data = private; + struct ekmf_key_info key = { 0 }; + int rc; + + if (cb_data->key_cb == NULL) { + pr_verbose(verbose, "No key callback function"); + return -EINVAL; + } + + rc = _ekmf_build_key_info(cb_data->config, curl, cb_data->login_token, + element, &key, false, cb_data->error_msg, + verbose); + if (rc != 0) { + pr_verbose(verbose, "Failed to build key info"); + goto out; + } + + rc = cb_data->key_cb(curl, &key, cb_data->cb_private); + if (rc != 0) { + pr_verbose(verbose, "Key callback rc: %d", rc); + goto out; + } + +out: + free_tag_list(&key.label_tags, false); + free_tag_list(&key.custom_tags, false); + free_export_control(&key.export_control, false); + + return rc; +} + +/** + * Builds the state URL parameter(s) from a comma separated list of states + * + * @param curl the curl handle + * @param states a comma separaed list of states + * + * @returns an allocated URL parameter value, or NULL in case of an error + */ +static char *_ekmf_build_state_filter(CURL *curl, const char *states) +{ + char *list, *tok, *ret = NULL, *tmp; + char *escaped_state; + + if (states == NULL) + goto error; + + list = strdup(states); + if (list == NULL) + goto error; + + tok = strtok(list, ","); + while (tok != NULL) { + escaped_state = curl_easy_escape(curl, tok, 0); + if (escaped_state == NULL) + goto error; + + if (asprintf(&tmp, "%s%s%s", ret != NULL ? ret : "", + ret == NULL ? "" : EKMF_URI_KEYS_LIST_STATE, + escaped_state) < 0) + tmp = NULL; + curl_free(escaped_state); + if (tmp == NULL) + goto error; + if (ret != NULL) + free(ret); + ret = tmp; + + tok = strtok(NULL, ","); + } + + free(list); + return ret; + +error: + if (ret != NULL) + free(ret); + return NULL; +} + +/** + * List available keys. The keys are ordered by name in ascending order. + * + * To perform a single request, set curl_handle to NULL. This will cause the + * function to initialize a new CURL handle, use it, and destroy it. + * If you plan to perform multiple requests to the same host, supply the address + * of a CURL pointer that is initially NULL. This function will then initialize + * a new CURL handle on the first call. On subsequent calls, pass in the address + * of the same CURL pointer so that the CURL handle is reused. After the last + * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). + * + * @param config the configuration structure + * @param curl_handle address of a CURL handle used for reusing the same + * CURL handle with multiple requests. + * @param key_cb a callback function that is called for each key + * found + * @param private a pointer that is passed as-is to the callback + * @param name_pattern a pattern to filter by name, or NULL to list all. + * @param states the states of the keys to list, or NULL to list keys + * in ACTIVE state only. Multiple states can be + * specified separated by comma. + * @param tags a list of custom tags to use as filter, or NULL + * @param error_msg on return: If not NULL, then a textual error message + * is returned in case of a failing request. The caller + * must free the error string when it is not NULL. + * @param verbose if true, verbose messages are printed + * + * @returns zero for success, a negative errno in case of an error. + * -EACCES is returned, if no or no valid login token is available. + * -EPERM is returned if the login token does not have permission to + * list the keys + */ +int ekmf_list_keys(const struct ekmf_config *config, CURL **curl_handle, + ekmf_key_cb_t key_cb, void *private, + const char *name_pattern, const char *states, + const struct ekmf_tag_list *tags, + char **error_msg, bool verbose) +{ + struct ekmf_key_cb_data_t cb_data; + char *escaped_name_pattern = NULL; + json_object *tags_obj = NULL; + char *state_filter = NULL; + char *escaped_tags = NULL; + char *login_token = NULL; + bool token_valid = false; + CURL *curl = NULL; + char *uri = NULL; + size_t i; + int rc; + + if (config == NULL || key_cb == NULL) + return -EINVAL; + + rc = ekmf_check_login_token(config, &token_valid, &login_token, + verbose); + if (rc != 0 || !token_valid) { + pr_verbose(verbose, "No valid login token available"); + rc = -EACCES; + goto out; + } + + rc = _ekmf_get_curl_handle(curl_handle, &curl); + if (rc != 0) { + pr_verbose(verbose, "Failed to get CURL handle"); + rc = -EIO; + goto out; + } + + cb_data.config = config; + cb_data.login_token = login_token; + cb_data.error_msg = error_msg; + cb_data.key_cb = key_cb; + cb_data.cb_private = private; + + escaped_name_pattern = curl_easy_escape(curl, name_pattern != NULL ? + name_pattern : "*", 0); + if (escaped_name_pattern == NULL) { + pr_verbose(verbose, "Failed to url-escape the name pattern"); + rc = -EIO; + goto out; + } + + state_filter = _ekmf_build_state_filter(curl, states != NULL ? states : + KEY_STATE_ACTIVE); + if (state_filter == NULL) { + pr_verbose(verbose, "Failed to build the state filter"); + rc = -EIO; + goto out; + } + + tags_obj = json_object_new_object(); + JSON_CHECK_ERROR(tags_obj == NULL, rc, -ENOMEM, + "Failed to generate JSON object", verbose, out); + for (i = 0; tags != NULL && i < tags->num_tags; i++) { + if (tags->tags[i].name == NULL || tags->tags[i].value == NULL) { + rc = -EINVAL; + goto out; + } + + rc = json_object_object_add_ex(tags_obj, tags->tags[i].name, + json_object_new_string( + tags->tags[i].value), + 0); + JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to " + "JSON object", verbose, out); + } + + escaped_tags = curl_easy_escape(curl, json_object_to_json_string_ext( + tags_obj, JSON_C_TO_STRING_PLAIN | + JSON_C_TO_STRING_NOSLASHESCAPE), 0); + if (escaped_tags == NULL) { + pr_verbose(verbose, "Failed to url-escape the tags"); + rc = -EIO; + goto out; + } + + if (asprintf(&uri, EKMF_URI_KEYS_LIST, state_filter, + ORDER_BY_LABEL_ASC, escaped_name_pattern, + escaped_tags) < 0) { + pr_verbose(verbose, "asprintf failed"); + rc = -ENOMEM; + goto out; + } + + rc = _ekmf_list_request(config, uri, curl, _ekmf_key_cb, + &cb_data, login_token, error_msg, verbose); + if (rc != 0) { + pr_verbose(verbose, "Failed to perform the list request"); + if (rc > 0) + rc = -EIO; + goto out; + } + +out: + _ekmf_release_curl_handle(curl_handle, curl); + + if (login_token != NULL) + free(login_token); + if (uri != NULL) + free(uri); + if (state_filter != NULL) + free(state_filter); + if (escaped_name_pattern != NULL) + curl_free(escaped_name_pattern); + if (escaped_tags != NULL) + curl_free(escaped_tags); + if (tags_obj != NULL) + json_object_put(tags_obj); + + return rc; +} + +/** + * Get information about a key by its UUID. + * + * To perform a single request, set curl_handle to NULL. This will cause the + * function to initialize a new CURL handle, use it, and destroy it. + * If you plan to perform multiple requests to the same host, supply the address + * of a CURL pointer that is initially NULL. This function will then initialize + * a new CURL handle on the first call. On subsequent calls, pass in the address + * of the same CURL pointer so that the CURL handle is reused. After the last + * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). + * + * @param config the configuration structure + * @param curl_handle address of a CURL handle used for reusing the same + * CURL handle with multiple requests. + * @param key_uuid the UUID of the key to get info for + * @param key an address of a key info pointer. On return + * the pointer is updated to point to a newly allocated + * key info struct. It must be freed by the caller + * using ekmf_free_key_info when no longer needed. + * @param error_msg on return: If not NULL, then a textual error message + * is returned in case of a failing request. The caller + * must free the error string when it is not NULL. + * @param verbose if true, verbose messages are printed + * + * @returns zero for success, a negative errno in case of an error. + * -EACCES is returned, if no or no valid login token is available. + * -EPERM is returned if the login token does not have permission to + * get the key info + */ +int ekmf_get_key_info(const struct ekmf_config *config, CURL **curl_handle, + const char *key_uuid, struct ekmf_key_info **key, + char **error_msg, bool verbose) +{ + json_object *response_obj = NULL; + char *escaped_uuid = NULL; + char *login_token = NULL; + bool token_valid = false; + CURL *curl = NULL; + char *uri = NULL; + long status_code; + int rc; + + if (config == NULL || key_uuid == NULL || key == NULL) + return -EINVAL; + + *key = NULL; + + rc = ekmf_check_login_token(config, &token_valid, &login_token, + verbose); + if (rc != 0 || !token_valid) { + pr_verbose(verbose, "No valid login token available"); + rc = -EACCES; + goto out; + } + + rc = _ekmf_get_curl_handle(curl_handle, &curl); + if (rc != 0) { + pr_verbose(verbose, "Failed to get CURL handle"); + rc = -EIO; + goto out; + } + + escaped_uuid = curl_easy_escape(curl, key_uuid, 0); + if (escaped_uuid == NULL) { + pr_verbose(verbose, "Failed to url-escape the key uuid"); + rc = -EIO; + goto out; + } + + if (asprintf(&uri, EKMF_URI_KEYS_GET, escaped_uuid) < 0) { + pr_verbose(verbose, "asprintf failed"); + rc = -ENOMEM; + goto out; + } + + rc = _ekmf_perform_request(config, uri, "GET", NULL, NULL, + login_token, &response_obj, NULL, + &status_code, error_msg, curl, verbose); + if (rc != 0) { + pr_verbose(verbose, "Failed perform the REST call"); + if (rc > 0) + rc = -EIO; + goto out; + } + + switch (status_code) { + case 200: + break; + case 400: + pr_verbose(verbose, "Bad request"); + rc = -EBADMSG; + goto out; + case 401: + pr_verbose(verbose, "Not authorized"); + rc = -EACCES; + goto out; + case 403: + pr_verbose(verbose, "Insufficient permissions"); + rc = -EPERM; + goto out; + case 404: + pr_verbose(verbose, "Not found"); + rc = -ENOENT; + goto out; + default: + pr_verbose(verbose, "REST Call failed with HTTP status code: " + "%ld", status_code); + rc = -EIO; + goto out; + } + + JSON_CHECK_OBJ(response_obj, json_type_object, rc, -EBADMSG, + "No or invalid response", verbose, out); + + *key = calloc(1, sizeof(struct ekmf_key_info)); + if (*key == NULL) { + pr_verbose(verbose, "calloc failed"); + rc = -ENOMEM; + goto out; + } + + rc = _ekmf_build_key_info(config, curl, login_token, response_obj, + *key, true, error_msg, verbose); + if (rc != 0) { + pr_verbose(verbose, "Failed to build template info"); + goto out; + } + +out: + _ekmf_release_curl_handle(curl_handle, curl); + + if (response_obj != NULL) + json_object_put(response_obj); + if (login_token != NULL) + free(login_token); + if (uri != NULL) + free(uri); + if (escaped_uuid != NULL) + curl_free(escaped_uuid); + if (rc != 0 && *key != NULL) { + free_key_info(*key); + free(*key); + *key = NULL; + } + + return rc; +} + +/** + * Clones a key info structure by making a deep copy of all strings and + * arrays. + * The copied key info must be freed using ekmf_free_key_info() by + * the caller. + * + * @param src the source key info structure + * @param dest the destination key info structure + * + * @returns zero for success, a negative errno in case of an error + */ +int ekmf_clone_key_info(const struct ekmf_key_info *src, + struct ekmf_key_info **dest) +{ + if (src == NULL || dest == NULL) + return -EINVAL; + + *dest = calloc(1, sizeof(struct ekmf_key_info)); + if (*dest == NULL) + return -ENOMEM; + + return clone_key_info(src, *dest); +} + +/** + * Free a key info structure. + * + * @param key the key info to free + */ +void ekmf_free_key_info(struct ekmf_key_info *key) +{ + free_key_info(key); + + free(key); +} + /** * Generate a secure identity key used to identify the client to EKMFWeb. * The secure key blob is stored in a file specified in field diff --git a/libekmfweb/libekmfweb.map b/libekmfweb/libekmfweb.map index 14e57c45..7170163f 100644 --- a/libekmfweb/libekmfweb.map +++ b/libekmfweb/libekmfweb.map @@ -14,6 +14,10 @@ LIBEKMFWEB_1.0 { ekmf_get_last_seq_no; ekmf_clone_template_info; ekmf_free_template_info; + ekmf_list_keys; + ekmf_get_key_info; + ekmf_clone_key_info; + ekmf_free_key_info; ekmf_curl_destroy; local: *; }; diff --git a/libekmfweb/utilities.c b/libekmfweb/utilities.c index ace48dbf..adaf22bd 100644 --- a/libekmfweb/utilities.c +++ b/libekmfweb/utilities.c @@ -927,6 +927,25 @@ static char *cond_strdup(const char *str, bool copy) return (char *)str; } +/** + * Returns the start of the UUId part of a href link. + * Returns NULL if href is NULL, or if the UUID is not found. + * The returned pointer (if not NULL) is within the specified href string! + */ +static const char *get_uuid_from_href(const char *href) +{ + const char *ch; + + if (href == NULL) + return NULL; + + ch = strrchr(href, '/'); + if (ch == NULL) + return NULL; + + return ch + 1; +} + /** * Builds a list of tag definitions from a JSON array. * @@ -1204,6 +1223,441 @@ void free_template_info(struct ekmf_template_info *template) free_tag_def_list(&template->label_tags, true); } +/** + * Builds a list of tags from a JSON array. + * + * @param array a JSON array of tags + * @param tag_list the tag list to build + * @param copy if true, the string values are copied (via strdup), + * if false, the string values re-use the JSON object's + * string buffer (see json_object_get_string). + * + * @returns zero for success, a negative errno in case of an error + */ +int json_build_tag_list(json_object *array, struct ekmf_tag_list *tag_list, + bool copy) +{ + json_object *obj; + size_t i; + int rc = 0; + + if (array == NULL || tag_list == NULL || + !json_object_is_type(array, json_type_array)) + return -EINVAL; + + tag_list->num_tags = json_object_array_length(array); + tag_list->tags = calloc(tag_list->num_tags, sizeof(struct ekmf_tag)); + if (tag_list->tags == NULL) + return -ENOMEM; + + for (i = 0; i < tag_list->num_tags; i++) { + obj = json_object_array_get_idx(array, i); + if (obj == NULL) + return -EBADMSG; + + tag_list->tags[i].name = cond_strdup( + json_get_string(obj, "name"), copy); + tag_list->tags[i].value = cond_strdup( + json_get_string(obj, "value"), copy); + + if (tag_list->tags[i].name == NULL || + tag_list->tags[i].value == NULL) { + rc = -ENOMEM; + goto out; + } + } + +out: + if (rc != 0) + free_tag_list(tag_list, copy); + + return rc; +} + +/** + * Clones (copies) a tag list + * + * @param src the source tag list + * @param dest the destination tag list + * + * @returns zero for success, a negative errno in case of an error + */ +int clone_tag_list(const struct ekmf_tag_list *src, + struct ekmf_tag_list *dest) +{ + size_t i; + int rc = 0; + + if (src == NULL || dest == NULL) + return -EINVAL; + + dest->num_tags = src->num_tags; + if (dest->num_tags == 0) { + dest->tags = NULL; + return 0; + } + + dest->tags = calloc(dest->num_tags, sizeof(struct ekmf_tag)); + if (dest->tags == NULL) + return -ENOMEM; + + for (i = 0; i < dest->num_tags; i++) { + dest->tags[i].name = cond_strdup(src->tags[i].name, true); + if (dest->tags[i].name == NULL) { + rc = -ENOMEM; + goto out; + } + dest->tags[i].value = cond_strdup(src->tags[i].value, true); + if (dest->tags[i].value == NULL) { + rc = -ENOMEM; + goto out; + } + } + +out: + if (rc != 0) + free_tag_list(dest, true); + return rc; +} + +/** + * Free a tag list + * + * @param tag_list the tag list to free + * @param free_tags if true, the tag name and value string s are + * freed, otherwise only the array is freed. + */ +void free_tag_list(struct ekmf_tag_list *tag_list, bool free_tags) +{ + size_t i; + + if (tag_list == NULL || tag_list->tags == NULL) + return; + + for (i = 0; free_tags && i < tag_list->num_tags; i++) { + free((char *)tag_list->tags[i].name); + free((char *)tag_list->tags[i].value); + } + + free(tag_list->tags); + tag_list->tags = NULL; + tag_list->num_tags = 0; +} + +/** + * Builds the export control information from a JSON object. + * + * @param export_control the JSON oibject + * @param tag_def_list the tag list to build + * @param copy if true, the string values are copied (via strdup), + * if false, the string values re-use the JSON object's + * string buffer (see json_object_get_string). + * + * @returns zero for success, a negative errno in case of an error + */ +int json_build_export_control(json_object *export_control, + struct ekmf_export_control *export_info, + bool copy) +{ + json_object *obj, *array; + size_t i; + int rc = 0; + + if (export_control == NULL || export_info == NULL || + !json_object_is_type(export_control, json_type_object)) + return -EINVAL; + + if (!json_object_object_get_ex(export_control, "exportAllowed", &obj) || + !json_object_is_type(obj, json_type_boolean)) + return -EINVAL; + + export_info->export_allowed = json_object_get_boolean(obj); + + if (!json_object_object_get_ex(export_control, "allowedKeys", &array) || + !json_object_is_type(array, json_type_array)) + return -EINVAL; + + export_info->num_exporting_keys = json_object_array_length(array); + export_info->exporting_keys = calloc(export_info->num_exporting_keys, + sizeof(struct ekmf_exporting_key)); + if (export_info->exporting_keys == NULL) + return -ENOMEM; + + for (i = 0; i < export_info->num_exporting_keys; i++) { + obj = json_object_array_get_idx(array, i); + if (obj == NULL) + return -EBADMSG; + + export_info->exporting_keys[i].name = cond_strdup( + json_get_string(obj, "title"), copy); + export_info->exporting_keys[i].uuid = cond_strdup( + get_uuid_from_href( + json_get_string(obj, "href")), copy); + + if (export_info->exporting_keys[i].name == NULL || + export_info->exporting_keys[i].uuid == NULL) { + rc = -ENOMEM; + goto out; + } + } + +out: + if (rc != 0) + free_export_control(export_info, copy); + + return rc; +} + +/** + * Clones (copies) an export control info + * + * @param src the source export control + * @param dest the destination export control + * + * @returns zero for success, a negative errno in case of an error + */ +int clone_export_control(const struct ekmf_export_control *src, + struct ekmf_export_control *dest) +{ + size_t i; + int rc = 0; + + if (src == NULL || dest == NULL) + return -EINVAL; + + dest->export_allowed = src->export_allowed; + + dest->num_exporting_keys = src->num_exporting_keys; + if (dest->num_exporting_keys == 0) { + dest->exporting_keys = NULL; + return 0; + } + + dest->exporting_keys = calloc(dest->num_exporting_keys, + sizeof(struct ekmf_exporting_key)); + if (dest->exporting_keys == NULL) + return -ENOMEM; + + for (i = 0; i < dest->num_exporting_keys; i++) { + dest->exporting_keys[i].name = + cond_strdup(src->exporting_keys[i].name, true); + if (dest->exporting_keys[i].name == NULL) { + rc = -ENOMEM; + goto out; + } + dest->exporting_keys[i].uuid = + cond_strdup(src->exporting_keys[i].uuid, true); + if (dest->exporting_keys[i].uuid == NULL) { + rc = -ENOMEM; + goto out; + } + } + +out: + if (rc != 0) + free_export_control(dest, true); + return rc; +} + +/** + * Free export control infos + * + * @param export_control the export control infos to free + * @param free_tags if true, the exporting keys name and uuid strings + * are freed, otherwise only the array is freed. + */ +void free_export_control(struct ekmf_export_control *export_control, + bool free_keys) +{ + size_t i; + + if (export_control == NULL || export_control->exporting_keys == NULL) + return; + + for (i = 0; free_keys && i < export_control->num_exporting_keys; i++) { + free((char *)export_control->exporting_keys[i].name); + free((char *)export_control->exporting_keys[i].uuid); + } + + free(export_control->exporting_keys); + export_control->exporting_keys = NULL; + export_control->num_exporting_keys = 0; +} + +/** + * Builds a key info structure from a JSON object. + * + * @param obj a JSON object containing the key info + * @param custom_tags a JSON array containing the custom tags + * @param export_control a JSON object containing the export_control infos + * @param key the key info struct to build + * @param copy if true, the string values are copied (via strdup), + * if false, the string values re-use the JSON object's + * string buffer (see json_object_get_string). + * + * @returns zero for success, a negative errno in case of an error + */ +int json_build_key_info(json_object *obj, json_object *custom_tags, + json_object *export_control, + struct ekmf_key_info *key, bool copy) +{ + json_object *field, *label_tags = NULL; + int rc = 0; + + if (obj == NULL || custom_tags == NULL || key == NULL || + !json_object_is_type(obj, json_type_object) || + !json_object_is_type(custom_tags, json_type_array)) + return -EINVAL; + + key->label = cond_strdup(json_get_string(obj, "label"), copy); + key->description = cond_strdup(json_get_string(obj, "description"), + copy); + key->uuid = cond_strdup(json_get_string(obj, "keyId"), copy); + key->key_type = cond_strdup(json_get_string(obj, "type"), copy); + key->algorithm = cond_strdup(json_get_string(obj, "algorithm"), copy); + if (json_object_object_get_ex(obj, "length", &field) && + json_object_is_type(field, json_type_int)) + key->key_size = json_object_get_int(field); + else + rc = -EBADMSG; + + key->state = cond_strdup(json_get_string(obj, "state"), copy); + key->keystore_type = cond_strdup(json_get_string(obj, "keystoreType"), + copy); + if (json_object_object_get_ex(obj, "template", &field) && + json_object_is_type(field, json_type_object)) { + key->template = cond_strdup(json_get_string(field, "title"), + copy); + key->template_uuid = cond_strdup(get_uuid_from_href( + json_get_string(field, "href")), copy); + } else { + rc = -EBADMSG; + } + key->activate_on = cond_strdup(json_get_string(obj, "activationDate"), + copy); + key->expires_on = cond_strdup(json_get_string(obj, "expirationDate"), + copy); + key->created_on = cond_strdup(json_get_string(obj, "createdOn"), copy); + key->updated_on = cond_strdup(json_get_string(obj, "updatedOn"), copy); + + if (rc != 0 || key->label == NULL || key->uuid == NULL || + key->algorithm == NULL || key->state == NULL || + key->keystore_type == NULL || key->template == NULL || + key->template_uuid == NULL || key->activate_on == NULL || + key->expires_on == NULL || key->created_on == NULL || + key->updated_on == NULL) { + rc = (rc != 0 ? rc : -ENOMEM); + goto out; + } + + json_object_object_get_ex(obj, "labelTags", &label_tags); + rc = json_build_tag_list(label_tags, &key->label_tags, copy); + if (rc != 0) + goto out; + + rc = json_build_tag_list(custom_tags, &key->custom_tags, copy); + if (rc != 0) + goto out; + + rc = json_build_export_control(export_control, &key->export_control, + copy); + if (rc != 0) + goto out; + +out: + if (rc != 0) { + free_tag_list(&key->label_tags, copy); + free_tag_list(&key->custom_tags, copy); + free_export_control(&key->export_control, copy); + if (copy) + free_key_info(key); + } + + return rc; +} + +/** + * Clones (copies) a key info structure + * + * @param src the source key info structure + * @param dest the destination key info structure + * + * @returns zero for success, a negative errno in case of an error + */ +int clone_key_info(const struct ekmf_key_info *src, + struct ekmf_key_info *dest) +{ + int rc; + + if (src == NULL || dest == NULL) + return -EINVAL; + + dest->label = cond_strdup(src->label, true); + dest->description = cond_strdup(src->description, true); + dest->uuid = cond_strdup(src->uuid, true); + dest->key_type = cond_strdup(src->key_type, true); + dest->algorithm = cond_strdup(src->algorithm, true); + dest->key_size = src->key_size; + dest->state = cond_strdup(src->state, true); + dest->keystore_type = cond_strdup(src->keystore_type, true); + dest->template = cond_strdup(src->template, true); + dest->template_uuid = cond_strdup(src->template_uuid, true); + dest->activate_on = cond_strdup(src->activate_on, true); + dest->expires_on = cond_strdup(src->expires_on, true); + dest->created_on = cond_strdup(src->created_on, true); + dest->updated_on = cond_strdup(src->updated_on, true); + if (dest->label == NULL || dest->uuid == NULL || + dest->algorithm == NULL || dest->state == NULL || + dest->keystore_type == NULL || dest->template == NULL || + dest->template_uuid == NULL || dest->activate_on == NULL || + dest->expires_on == NULL || dest->created_on == NULL || + dest->updated_on == NULL) { + rc = -ENOMEM; + goto out; + } + + rc = clone_tag_list(&src->label_tags, &dest->label_tags); + if (rc != 0) + goto out; + + rc = clone_tag_list(&src->custom_tags, &dest->custom_tags); + if (rc != 0) + goto out; +out: + if (rc != 0) + free_key_info(dest); + return rc; +} + +/** + * Free a key info structure + * + * @param key the key info to free + */ +void free_key_info(struct ekmf_key_info *key) +{ + if (key == NULL) + return; + + free((char *)key->label); + free((char *)key->description); + free((char *)key->uuid); + free((char *)key->key_type); + free((char *)key->algorithm); + free((char *)key->state); + free((char *)key->keystore_type); + free((char *)key->template); + free((char *)key->template_uuid); + free((char *)key->activate_on); + free((char *)key->expires_on); + free((char *)key->created_on); + free((char *)key->updated_on); + + free_tag_list(&key->label_tags, true); + free_tag_list(&key->custom_tags, true); + free_export_control(&key->export_control, true); +} + struct ecc_curve_info { int curve_nid; enum { diff --git a/libekmfweb/utilities.h b/libekmfweb/utilities.h index 71bbbb47..85c06cca 100644 --- a/libekmfweb/utilities.h +++ b/libekmfweb/utilities.h @@ -55,6 +55,27 @@ int clone_template_info(const struct ekmf_template_info *src, struct ekmf_template_info *dest); void free_template_info(struct ekmf_template_info *template); +int json_build_tag_list(json_object *array, struct ekmf_tag_list *tag_list, + bool copy); +int clone_tag_list(const struct ekmf_tag_list *src, + struct ekmf_tag_list *dest); +void free_tag_list(struct ekmf_tag_list *tag_list, bool free_tags); + +int json_build_export_control(json_object *export_control, + struct ekmf_export_control *export_info, + bool copy); +int clone_export_control(const struct ekmf_export_control *src, + struct ekmf_export_control *dest); +void free_export_control(struct ekmf_export_control *export_control, + bool free_keys); + +int json_build_key_info(json_object *obj, json_object *custom_tags, + json_object *export_control, + struct ekmf_key_info *key, bool copy); +int clone_key_info(const struct ekmf_key_info *src, + struct ekmf_key_info *dest); +void free_key_info(struct ekmf_key_info *key); + size_t ecc_get_curve_prime_bits(int curve_nid); size_t ecc_get_curve_prime_length(int curve_nid); const char *ecc_get_curve_id(int curve_nid);