summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2018-03-20 11:14:26 +0100
committerWerner Koch <wk@gnupg.org>2018-03-20 11:14:26 +0100
commit6073789a6d3514263404c93fa795a398bfd93d91 (patch)
treeb5a016f79e71a0f294348928a9c8ee092d34c80b
parent44f9e80ea99733f373d75c3632273f763e6f5853 (diff)
downloadgpgme-6073789a6d3514263404c93fa795a398bfd93d91.tar.gz
json: Implement op:encrypt
Signed-off-by: Werner Koch <wk@gnupg.org>
-rw-r--r--src/gpgme-json.c454
1 files changed, 393 insertions, 61 deletions
diff --git a/src/gpgme-json.c b/src/gpgme-json.c
index 4251f2b3..00d81105 100644
--- a/src/gpgme-json.c
+++ b/src/gpgme-json.c
@@ -46,10 +46,10 @@
static void xoutofcore (const char *type) GPGRT_ATTR_NORETURN;
-static cjson_t error_object_v (const char *message,
- va_list arg_ptr) GPGRT_ATTR_PRINTF(1,0);
-static cjson_t error_object (const char *message,
- ...) GPGRT_ATTR_PRINTF(1,2);
+static cjson_t error_object_v (cjson_t json, const char *message,
+ va_list arg_ptr) GPGRT_ATTR_PRINTF(2,0);
+static cjson_t error_object (cjson_t json, const char *message,
+ ...) GPGRT_ATTR_PRINTF(2,3);
static char *error_object_string (const char *message,
...) GPGRT_ATTR_PRINTF(1,2);
@@ -64,6 +64,16 @@ static int opt_interactive;
*/
#define xtrymalloc(a) gpgrt_malloc ((a))
+#define xmalloc(a) ({ \
+ void *_r = gpgrt_malloc ((a)); \
+ if (!_r) \
+ xoutofcore ("malloc"); \
+ _r; })
+#define xcalloc(a,b) ({ \
+ void *_r = gpgrt_calloc ((a), (b)); \
+ if (!_r) \
+ xoutofcore ("calloc"); \
+ _r; })
#define xstrdup(a) ({ \
char *_r = gpgrt_strdup ((a)); \
if (!_r) \
@@ -100,7 +110,7 @@ xjson_CreateObject (void)
/* Wrapper around cJSON_AddStringToObject which returns an gpg-error
- * code instead of the NULL or the object. */
+ * code instead of the NULL or the new object. */
static gpg_error_t
cjson_AddStringToObject (cjson_t object, const char *name, const char *string)
{
@@ -111,7 +121,7 @@ cjson_AddStringToObject (cjson_t object, const char *name, const char *string)
/* Same as cjson_AddStringToObject but prints an error message and
- * terminates. the process. */
+ * terminates the process. */
static void
xjson_AddStringToObject (cjson_t object, const char *name, const char *string)
{
@@ -120,19 +130,40 @@ xjson_AddStringToObject (cjson_t object, const char *name, const char *string)
}
-/* Create a JSON error object. */
+/* Wrapper around cJSON_AddBoolToObject which terminates the process
+ * in case of an error. */
+static void
+xjson_AddBoolToObject (cjson_t object, const char *name, int abool)
+{
+ if (!cJSON_AddBoolToObject (object, name, abool))
+ xoutofcore ("cJSON_AddStringToObject");
+ return ;
+}
+
+
+/* Create a JSON error object. If JSON is not NULL the error message
+ * is appended to that object. An existing "type" item will be replaced. */
static cjson_t
-error_object_v (const char *message, va_list arg_ptr)
+error_object_v (cjson_t json, const char *message, va_list arg_ptr)
{
- cjson_t response;
+ cjson_t response, j_tmp;
char *msg;
msg = gpgrt_vbsprintf (message, arg_ptr);
if (!msg)
xoutofcore ("error_object");
- response = xjson_CreateObject ();
- xjson_AddStringToObject (response, "type", "error");
+ response = json? json : xjson_CreateObject ();
+
+ if (!(j_tmp = cJSON_GetObjectItem (response, "type")))
+ xjson_AddStringToObject (response, "type", "error");
+ else /* Replace existing "type". */
+ {
+ j_tmp = cJSON_CreateString ("error");
+ if (!j_tmp)
+ xoutofcore ("cJSON_CreateString");
+ cJSON_ReplaceItemInObject (response, "type", j_tmp);
+ }
xjson_AddStringToObject (response, "msg", msg);
xfree (msg);
@@ -153,13 +184,13 @@ xjson_Print (cjson_t object)
static cjson_t
-error_object (const char *message, ...)
+error_object (cjson_t json, const char *message, ...)
{
cjson_t response;
va_list arg_ptr;
va_start (arg_ptr, message);
- response = error_object_v (message, arg_ptr);
+ response = error_object_v (json, message, arg_ptr);
va_end (arg_ptr);
return response;
}
@@ -173,7 +204,7 @@ error_object_string (const char *message, ...)
char *msg;
va_start (arg_ptr, message);
- response = error_object_v (message, arg_ptr);
+ response = error_object_v (NULL, message, arg_ptr);
va_end (arg_ptr);
msg = xjson_Print (response);
@@ -182,6 +213,179 @@ error_object_string (const char *message, ...)
}
+/* Get the boolean property NAME from the JSON object and store true
+ * or valse at R_VALUE. If the name is unknown the value of DEF_VALUE
+ * is returned. If the type of the value is not boolean,
+ * GPG_ERR_INV_VALUE is returned and R_VALUE set to DEF_VALUE. */
+static gpg_error_t
+get_boolean_flag (cjson_t json, const char *name, int def_value, int *r_value)
+{
+ cjson_t j_item;
+
+ j_item = cJSON_GetObjectItem (json, name);
+ if (!j_item)
+ *r_value = def_value;
+ else if (cjson_is_true (j_item))
+ *r_value = 1;
+ else if (cjson_is_false (j_item))
+ *r_value = 0;
+ else
+ {
+ *r_value = def_value;
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+
+ return 0;
+}
+
+
+/* Get the boolean property PROTOCOL from the JSON object and store
+ * its value at R_PROTOCOL. The default is OpenPGP. */
+static gpg_error_t
+get_protocol (cjson_t json, gpgme_protocol_t *r_protocol)
+{
+ cjson_t j_item;
+
+ *r_protocol = GPGME_PROTOCOL_OpenPGP;
+ j_item = cJSON_GetObjectItem (json, "protocol");
+ if (!j_item)
+ ;
+ else if (!cjson_is_string (j_item))
+ return gpg_error (GPG_ERR_INV_VALUE);
+ else if (!strcmp(j_item->valuestring, "openpgp"))
+ ;
+ else if (!strcmp(j_item->valuestring, "cms"))
+ *r_protocol = GPGME_PROTOCOL_CMS;
+ else
+ return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
+
+ return 0;
+}
+
+
+/* Extract the keys from the KEYS array in the JSON object. CTX is a
+ * GPGME context object. On success an array with the keys is stored
+ * at R_KEYS. In failure an error code is returned. */
+static gpg_error_t
+get_keys (gpgme_ctx_t ctx, cjson_t json, gpgme_key_t **r_keys)
+{
+ gpg_error_t err;
+ cjson_t j_keys, j_item;
+ int i, nkeys;
+ gpgme_key_t *keys;
+
+ *r_keys = NULL;
+
+ j_keys = cJSON_GetObjectItem (json, "keys");
+ if (!j_keys)
+ return gpg_error (GPG_ERR_NO_KEY);
+ if (!cjson_is_array (j_keys) && !cjson_is_string (j_keys))
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (cjson_is_string (j_keys))
+ nkeys = 1;
+ else
+ {
+ nkeys = cJSON_GetArraySize (j_keys);
+ if (!nkeys)
+ return gpg_error (GPG_ERR_NO_KEY);
+ for (i=0; i < nkeys; i++)
+ {
+ j_item = cJSON_GetArrayItem (j_keys, i);
+ if (!j_item || !cjson_is_string (j_item))
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+ }
+
+ /* Now allocate an array to store the gpgme key objects. */
+ keys = xcalloc (nkeys + 1, sizeof *keys);
+
+ if (cjson_is_string (j_keys))
+ {
+ err = gpgme_get_key (ctx, j_keys->valuestring, &keys[0], 0);
+ if (err)
+ goto leave;
+ }
+ else
+ {
+ for (i=0; i < nkeys; i++)
+ {
+ j_item = cJSON_GetArrayItem (j_keys, i);
+ err = gpgme_get_key (ctx, j_item->valuestring, &keys[i], 0);
+ if (err)
+ goto leave;
+ }
+ }
+ err = 0;
+ *r_keys = keys;
+ keys = NULL;
+
+ leave:
+ if (keys)
+ {
+ for (i=0; keys[i]; i++)
+ gpgme_key_unref (keys[i]);
+ xfree (keys);
+ }
+ return err;
+}
+
+
+
+/*
+ * GPGME support functions.
+ */
+
+/* Helper for get_context. */
+static gpgme_ctx_t
+_create_new_context (gpgme_protocol_t proto)
+{
+ gpg_error_t err;
+ gpgme_ctx_t ctx;
+
+ err = gpgme_new (&ctx);
+ if (err)
+ log_fatal ("error creating GPGME context: %s\n", gpg_strerror (err));
+ gpgme_set_protocol (ctx, proto);
+ return ctx;
+}
+
+
+/* Return a context object for protocol PROTO. This is currently a
+ * statuically allocated context initialized for PROTO. Termnates
+ * process on failure. */
+static gpgme_ctx_t
+get_context (gpgme_protocol_t proto)
+{
+ static gpgme_ctx_t ctx_openpgp, ctx_cms;
+
+ if (proto == GPGME_PROTOCOL_OpenPGP)
+ {
+ if (!ctx_openpgp)
+ ctx_openpgp = _create_new_context (proto);
+ return ctx_openpgp;
+ }
+ else if (proto == GPGME_PROTOCOL_CMS)
+ {
+ if (!ctx_cms)
+ ctx_cms = _create_new_context (proto);
+ return ctx_cms;
+ }
+ else
+ log_bug ("invalid protocol %d requested\n", proto);
+}
+
+
+
+/* Free context object retrieved by get_context. */
+static void
+release_context (gpgme_ctx_t ctx)
+{
+ /* Nothing to do right now. */
+ (void)ctx;
+}
+
+
/*
* Implementaion of the commands.
@@ -193,12 +397,13 @@ static const char hlp_encrypt[] =
"keys: Array of strings with the fingerprints or user-ids\n"
" of the keys to encrypt the data. For a single key\n"
" a String may be used instead of an array.\n"
- "data: Base64 encoded input data.\n"
+ "data: Input data. \n"
"\n"
"Optional parameters:\n"
"protocol: Either \"openpgp\" (default) or \"cms\".\n"
"\n"
"Optional boolean flags (default is false):\n"
+ "base64: Input data is base64 encoded.\n"
"armor: Request output in armored format.\n"
"always-trust: Request --always-trust option.\n"
"no-encrypt-to: Do not use a default recipient.\n"
@@ -213,11 +418,144 @@ static const char hlp_encrypt[] =
" OpenPGP or a PEM message.\n"
"base64: Boolean indicating whether data is base64 encoded.";
static gpg_error_t
-op_encrypt (cjson_t request, cjson_t *r_result)
+op_encrypt (cjson_t request, cjson_t result)
{
+ gpg_error_t err;
+ gpgme_ctx_t ctx = NULL;
+ gpgme_protocol_t protocol;
+ int opt_base64;
+ gpgme_key_t *keys = NULL;
+ cjson_t j_input;
+ gpgme_data_t input = NULL;
+ gpgme_data_t output = NULL;
+ int abool, i;
+ gpgme_encrypt_flags_t encrypt_flags = 0;
+
+ if ((err = get_protocol (request, &protocol)))
+ goto leave;
+ ctx = get_context (protocol);
+
+ if ((err = get_boolean_flag (request, "base64", 0, &opt_base64)))
+ goto leave;
+
+ if ((err = get_boolean_flag (request, "armor", 0, &abool)))
+ goto leave;
+ gpgme_set_armor (ctx, abool);
+ if ((err = get_boolean_flag (request, "always-trust", 0, &abool)))
+ goto leave;
+ if (abool)
+ encrypt_flags |= GPGME_ENCRYPT_ALWAYS_TRUST;
+ if ((err = get_boolean_flag (request, "no-encrypt-to", 0,&abool)))
+ goto leave;
+ if (abool)
+ encrypt_flags |= GPGME_ENCRYPT_NO_ENCRYPT_TO;
+ if ((err = get_boolean_flag (request, "no-compress", 0, &abool)))
+ goto leave;
+ if (abool)
+ encrypt_flags |= GPGME_ENCRYPT_NO_COMPRESS;
+ if ((err = get_boolean_flag (request, "throw-keyids", 0, &abool)))
+ goto leave;
+ if (abool)
+ encrypt_flags |= GPGME_ENCRYPT_THROW_KEYIDS;
+ if ((err = get_boolean_flag (request, "wrap", 0, &abool)))
+ goto leave;
+ if (abool)
+ encrypt_flags |= GPGME_ENCRYPT_WRAP;
+
+
+ /* Get the keys. */
+ err = get_keys (ctx, request, &keys);
+ if (err)
+ {
+ /* Provide a custom error response. */
+ error_object (result, "Error getting keys: %s", gpg_strerror (err));
+ goto leave;
+ }
+ /* Get the data. Note that INPUT is a shallow data object with the
+ * storage hold in REQUEST. */
+ j_input = cJSON_GetObjectItem (request, "data");
+ if (!j_input)
+ {
+ err = gpg_error (GPG_ERR_NO_DATA);
+ goto leave;
+ }
+ if (!cjson_is_string (j_input))
+ {
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+ if (opt_base64)
+ {
+ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ goto leave;
+ }
+ err = gpgme_data_new_from_mem (&input, j_input->valuestring,
+ strlen (j_input->valuestring), 0);
+ if (err)
+ {
+ error_object (result, "Error creating input data object: %s",
+ gpg_strerror (err));
+ goto leave;
+ }
- return 0;
+ /* Create an output data object. */
+ err = gpgme_data_new (&output);
+ if (err)
+ {
+ error_object (result, "Error creating output data object: %s",
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Encrypt. */
+ err = gpgme_op_encrypt (ctx, keys, encrypt_flags, input, output);
+ /* encrypt_result = gpgme_op_encrypt_result (ctx); */
+ if (err)
+ {
+ error_object (result, "Encryption failed: %s", gpg_strerror (err));
+ goto leave;
+ }
+ gpgme_data_release (input);
+ input = NULL;
+
+ xjson_AddStringToObject (result, "type", "ciphertext");
+ /* If armoring is used we do not need to base64 the output. */
+ xjson_AddBoolToObject (result, "base64", !gpgme_get_armor (ctx));
+ if (gpgme_get_armor (ctx))
+ {
+ char *buffer;
+
+ /* Make sure that we really have a string. */
+ gpgme_data_write (output, "", 1);
+ buffer = gpgme_data_release_and_get_mem (output, NULL);
+ if (!buffer)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ err = cjson_AddStringToObject (result, "data", buffer);
+ gpgme_free (buffer);
+ if (err)
+ goto leave;
+ }
+ else
+ {
+ error_object (result, "Binary output is not yet supported");
+ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ goto leave;
+ }
+
+ leave:
+ if (keys)
+ {
+ for (i=0; keys[i]; i++)
+ gpgme_key_unref (keys[i]);
+ xfree (keys);
+ }
+ release_context (ctx);
+ gpgme_data_release (input);
+ return err;
}
@@ -233,10 +571,8 @@ static const char hlp_help[] =
" encrypt Encrypt data.\n"
" help Help overview.";
static gpg_error_t
-op_help (cjson_t request, cjson_t *r_result)
+op_help (cjson_t request, cjson_t result)
{
- gpg_error_t err = 0;
- cjson_t result = NULL;
cjson_t j_tmp;
char *buffer = NULL;
const char *msg;
@@ -247,20 +583,11 @@ op_help (cjson_t request, cjson_t *r_result)
else
msg = hlp_help;
- result = cJSON_CreateObject ();
- if (!result)
- err = gpg_error_from_syserror ();
- if (!err)
- err = cjson_AddStringToObject (result, "type", "help");
- if (!err)
- err = cjson_AddStringToObject (result, "msg", msg);
+ xjson_AddStringToObject (result, "type", "help");
+ xjson_AddStringToObject (result, "msg", msg);
xfree (buffer);
- if (err)
- xfree (result);
- else
- *r_result = result;
- return err;
+ return 0;
}
@@ -272,7 +599,7 @@ process_request (const char *request)
{
static struct {
const char *op;
- gpg_error_t (*handler)(cjson_t request, cjson_t *r_result);
+ gpg_error_t (*handler)(cjson_t request, cjson_t result);
const char * const helpstr;
} optbl[] = {
{ "encrypt", op_encrypt, hlp_encrypt },
@@ -281,22 +608,23 @@ process_request (const char *request)
{ "help", op_help, hlp_help },
{ NULL }
};
- gpg_error_t err = 0;
size_t erroff;
cjson_t json;
cjson_t j_tmp, j_op;
- cjson_t response = NULL;
+ cjson_t response;
int helpmode;
const char *op;
char *res;
int idx;
+ response = xjson_CreateObject ();
+
json = cJSON_Parse (request, &erroff);
if (!json)
{
log_string (GPGRT_LOGLVL_INFO, request);
log_info ("invalid JSON object at offset %zu\n", erroff);
- response = error_object ("invalid JSON object at offset %zu\n", erroff);
+ error_object (response, "invalid JSON object at offset %zu\n", erroff);
goto leave;
}
@@ -308,7 +636,7 @@ process_request (const char *request)
{
if (!helpmode)
{
- response = error_object ("Property \"op\" missing");
+ error_object (response, "Property \"op\" missing");
goto leave;
}
op = "help"; /* Help summary. */
@@ -323,40 +651,44 @@ process_request (const char *request)
{
if (helpmode && strcmp (op, "help"))
{
- response = cJSON_CreateObject ();
- if (!response)
- err = gpg_error_from_syserror ();
- if (!err)
- err = cjson_AddStringToObject (response, "type", "help");
- if (!err)
- err = cjson_AddStringToObject (response, "op", op);
- if (!err)
- err = cjson_AddStringToObject (response, "msg", optbl[idx].helpstr);
+ xjson_AddStringToObject (response, "type", "help");
+ xjson_AddStringToObject (response, "op", op);
+ xjson_AddStringToObject (response, "msg", optbl[idx].helpstr);
}
else
- err = optbl[idx].handler (json, &response);
+ {
+ gpg_error_t err;
+
+ err = optbl[idx].handler (json, response);
+ if (err)
+ {
+ if (!(j_tmp = cJSON_GetObjectItem (response, "type"))
+ || !cjson_is_string (j_tmp)
+ || strcmp (j_tmp->valuestring, "error"))
+ {
+ /* No error type response - provide a generic one. */
+ error_object (response, "Operation failed: %s",
+ gpg_strerror (err));
+ }
+
+ xjson_AddStringToObject (response, "op", op);
+ }
+
+ }
}
else /* Operation not supported. */
{
- response = error_object ("Unknown operation '%s'", op);
- err = cjson_AddStringToObject (response, "op", op);
+ error_object (response, "Unknown operation '%s'", op);
+ xjson_AddStringToObject (response, "op", op);
}
leave:
cJSON_Delete (json);
json = NULL;
- if (err)
- log_error ("failed to create the response: %s\n", gpg_strerror (err));
- if (response)
- {
- res = cJSON_Print (response);
- if (!res)
- log_error ("Printing JSON data failed\n");
- cJSON_Delete (response);
- }
- else
- res = NULL;
-
+ res = cJSON_Print (response);
+ if (!res)
+ log_error ("Printing JSON data failed\n");
+ cJSON_Delete (response);
return res;
}