static gpgme_error_t gpgsm_keylist(void *engine, const char *pattern, int secret_only, gpgme_keylist_mode_t mode) { engine_gpgsm_t gpgsm = engine; char *line; gpgme_error_t err; int list_mode = 0; if(mode & GPGME_KEYLIST_MODE_LOCAL) list_mode |= 1; if(mode & GPGME_KEYLIST_MODE_EXTERN) list_mode |= 2; if(!pattern) pattern = ""; /* Always send list-mode option because RESET does not reset it. */ if(asprintf(&line, "OPTION list-mode=%d", (list_mode & 3)) < 0) return gpg_error_from_errno(errno); err = gpgsm_assuan_simple_command(gpgsm->assuan_ctx, line, NULL, NULL); free(line); if(err) return err; /* Always send key validation because RESET does not reset it. */ /* Use the validation mode if required. We don't check for an error yet because this is a pretty fresh gpgsm features. */ gpgsm_assuan_simple_command(gpgsm->assuan_ctx, (mode & GPGME_KEYLIST_MODE_VALIDATE) ? "OPTION with-validation=1" : "OPTION with-validation=0" , NULL, NULL); /* Length is "LISTSECRETKEYS " + p + '\0'. */ line = malloc(15 + strlen(pattern) + 1); if(!line) return gpg_error_from_errno(errno); if(secret_only) { strcpy(line, "LISTSECRETKEYS "); strcpy(&line[15], pattern); } else { strcpy(line, "LISTKEYS "); strcpy(&line[9], pattern); } gpgsm_clear_fd(gpgsm, INPUT_FD); gpgsm_clear_fd(gpgsm, OUTPUT_FD); gpgsm_clear_fd(gpgsm, MESSAGE_FD); err = start(gpgsm, line); free(line); return err; }
static gpgme_error_t set_recipients(engine_gpgsm_t gpgsm, gpgme_key_t recp[]) { gpgme_error_t err = 0; assuan_context_t ctx = gpgsm->assuan_ctx; char *line; int linelen; int invalid_recipients = 0; int i = 0; linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */ line = malloc(10 + 40 + 1); if(!line) return gpg_error_from_errno(errno); strcpy(line, "RECIPIENT "); while(!err && recp[i]) { char *fpr; int newlen; if(!recp[i]->subkeys || !recp[i]->subkeys->fpr) { invalid_recipients++; continue; } fpr = recp[i]->subkeys->fpr; newlen = 11 + strlen(fpr); if(linelen < newlen) { char *newline = realloc(line, newlen); if(! newline) { int saved_errno = errno; free(line); return gpg_error_from_errno(saved_errno); } line = newline; linelen = newlen; } strcpy(&line[10], fpr); err = gpgsm_assuan_simple_command(ctx, line, gpgsm->status.fnc, gpgsm->status.fnc_value); /* FIXME: This requires more work. */ if(gpg_err_code(err) == GPG_ERR_NO_PUBKEY) invalid_recipients++; else if(err) { free(line); return err; } i++; } free(line); return gpg_error(invalid_recipients ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR); }
static gpgme_error_t gpgsm_reset(void *engine) { engine_gpgsm_t gpgsm = engine; /* We must send a reset because we need to reset the list of signers. Note that RESET does not reset OPTION commands. */ return gpgsm_assuan_simple_command(gpgsm->assuan_ctx, "RESET", NULL, NULL); }
static gpgme_error_t gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (!recp) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); if (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO) { err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "OPTION no-encrypt-to", NULL, NULL); if (err) return err; } gpgsm->input_cb.data = plain; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; gpgsm->output_cb.data = ciph; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (gpgsm->output_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = set_recipients (gpgsm, recp); if (!err) err = start (gpgsm, "ENCRYPT"); return err; }
static gpgme_error_t gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt) { gpg_error_t err = 0; char line[COMMANDLINELEN]; char *which; iocb_data_t *iocb_data; int dir; switch (fd_type) { case INPUT_FD: which = "INPUT"; iocb_data = &gpgsm->input_cb; break; case OUTPUT_FD: which = "OUTPUT"; iocb_data = &gpgsm->output_cb; break; case MESSAGE_FD: which = "MESSAGE"; iocb_data = &gpgsm->message_cb; break; default: return gpg_error (GPG_ERR_INV_VALUE); } dir = iocb_data->dir; #if USE_DESCRIPTOR_PASSING /* We try to short-cut the communication by giving GPGSM direct access to the file descriptor, rather than using a pipe. */ iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data); if (iocb_data->server_fd < 0) { int fds[2]; if (_gpgme_io_pipe (fds, dir) < 0) return gpg_error_from_syserror (); iocb_data->fd = dir ? fds[0] : fds[1]; iocb_data->server_fd = dir ? fds[1] : fds[0]; if (_gpgme_io_set_close_notify (iocb_data->fd, close_notify_handler, gpgsm)) { err = gpg_error (GPG_ERR_GENERAL); goto leave_set_fd; } } err = assuan_sendfd (gpgsm->assuan_ctx, iocb_data->server_fd); if (err) goto leave_set_fd; _gpgme_io_close (iocb_data->server_fd); iocb_data->server_fd = -1; if (opt) snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt); else snprintf (line, COMMANDLINELEN, "%s FD", which); #else if (opt) snprintf (line, COMMANDLINELEN, "%s FD=%s %s", which, iocb_data->server_fd_str, opt); else snprintf (line, COMMANDLINELEN, "%s FD=%s", which, iocb_data->server_fd_str); #endif err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL); #if USE_DESCRIPTOR_PASSING leave_set_fd: if (err) { _gpgme_io_close (iocb_data->fd); iocb_data->fd = -1; if (iocb_data->server_fd != -1) { _gpgme_io_close (iocb_data->server_fd); iocb_data->server_fd = -1; } } #endif return err; }
static gpgme_error_t gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out, gpgme_sig_mode_t mode, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; char *assuan_cmd; int i; gpgme_key_t key; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); /* FIXME: This does not work as RESET does not reset it so we can't revert back to default. */ if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT) { /* FIXME: Make sure that if we run multiple operations, that we can reset any previously set value in case the default is requested. */ if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0) return gpg_error_from_syserror (); err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd, NULL, NULL); free (assuan_cmd); if (err) return err; } for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++) { const char *s = key->subkeys ? key->subkeys->fpr : NULL; if (s && strlen (s) < 80) { char buf[100]; strcpy (stpcpy (buf, "SIGNER "), s); err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf, gpgsm->status.fnc, gpgsm->status.fnc_value); } else err = gpg_error (GPG_ERR_INV_VALUE); gpgme_key_unref (key); if (err) return err; } gpgsm->input_cb.data = in; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; gpgsm->output_cb.data = out; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (gpgsm->output_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH ? "SIGN --detached" : "SIGN"); return err; }
static gpgme_error_t gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only, int reserved, gpgme_keylist_mode_t mode) { engine_gpgsm_t gpgsm = engine; char *line; gpgme_error_t err; /* Length is "LISTSECRETKEYS " + p + '\0'. */ int length = 15 + 1; char *linep; int any_pattern = 0; int list_mode = 0; if (reserved) return gpg_error (GPG_ERR_INV_VALUE); if (mode & GPGME_KEYLIST_MODE_LOCAL) list_mode |= 1; if (mode & GPGME_KEYLIST_MODE_EXTERN) list_mode |= 2; /* Always send list-mode option because RESET does not reset it. */ if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0) return gpg_error_from_syserror (); err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL); free (line); if (err) return err; /* Always send key validation because RESET does not reset it. */ /* Use the validation mode if required. We don't check for an error yet because this is a pretty fresh gpgsm features. */ gpgsm_assuan_simple_command (gpgsm->assuan_ctx, (mode & GPGME_KEYLIST_MODE_VALIDATE)? "OPTION with-validation=1": "OPTION with-validation=0" , NULL, NULL); if (pattern && *pattern) { const char **pat = pattern; while (*pat) { const char *patlet = *pat; while (*patlet) { length++; if (*patlet == '%' || *patlet == ' ' || *patlet == '+') length += 2; patlet++; } pat++; length++; } } line = malloc (length); if (!line) return gpg_error_from_syserror (); if (secret_only) { strcpy (line, "LISTSECRETKEYS "); linep = &line[15]; } else { strcpy (line, "LISTKEYS "); linep = &line[9]; } if (pattern && *pattern) { while (*pattern) { const char *patlet = *pattern; while (*patlet) { switch (*patlet) { case '%': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '5'; break; case ' ': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '0'; break; case '+': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = 'B'; break; default: *(linep++) = *patlet; break; } patlet++; } any_pattern = 1; *linep++ = ' '; pattern++; } } if (any_pattern) linep--; *linep = '\0'; gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, line); free (line); return err; }
static gpgme_error_t gpgsm_keylist (void *engine, const char *pattern, int secret_only, gpgme_keylist_mode_t mode) { engine_gpgsm_t gpgsm = engine; char *line; gpgme_error_t err; int list_mode = 0; if (mode & GPGME_KEYLIST_MODE_LOCAL) list_mode |= 1; if (mode & GPGME_KEYLIST_MODE_EXTERN) list_mode |= 2; if (!pattern) pattern = ""; /* Hack to make sure that the agent is started. Only if the agent has been started an application may connect to the agent via GPGME_PROTOCOL_ASSUAN - for example to look for smartcards. We do this only if a secret key listing has been requested. In general this is not needed because a secret key listing starts the agent. However on a fresh installation no public keys are available and thus there is no need for gpgsm to ask the agent whether a secret key exists for the public key. */ if (secret_only) gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "GETINFO agent-check", NULL, NULL); /* Always send list-mode option because RESET does not reset it. */ if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0) return gpg_error_from_syserror (); err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL); free (line); if (err) return err; /* Always send key validation because RESET does not reset it. */ /* Use the validation mode if requested. We don't check for an error yet because this is a pretty fresh gpgsm features. */ gpgsm_assuan_simple_command (gpgsm->assuan_ctx, (mode & GPGME_KEYLIST_MODE_VALIDATE)? "OPTION with-validation=1": "OPTION with-validation=0" , NULL, NULL); /* Include the ephemeral keys if requested. We don't check for an error yet because this is a pretty fresh gpgsm features. */ gpgsm_assuan_simple_command (gpgsm->assuan_ctx, (mode & GPGME_KEYLIST_MODE_EPHEMERAL)? "OPTION with-ephemeral-keys=1": "OPTION with-ephemeral-keys=0" , NULL, NULL); /* Length is "LISTSECRETKEYS " + p + '\0'. */ line = malloc (15 + strlen (pattern) + 1); if (!line) return gpg_error_from_syserror (); if (secret_only) { strcpy (line, "LISTSECRETKEYS "); strcpy (&line[15], pattern); } else { strcpy (line, "LISTKEYS "); strcpy (&line[9], pattern); } gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, line); free (line); return err; }
static gpgme_error_t gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; gpgme_data_encoding_t dataenc; int idx; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (keydata && keyarray) return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed. */ dataenc = gpgme_data_get_encoding (keydata); if (keyarray) { size_t buflen; char *buffer, *p; /* Fist check whether the engine already features the --re-import option. */ err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "GETINFO cmd_has_option IMPORT re-import", NULL, NULL); if (err) return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Create an internal data object with a list of all fingerprints. The data object and its memory (to avoid an extra copy by gpgme_data_new_from_mem) are stored in two variables which are released by the close_notify_handler. */ for (idx=0, buflen=0; keyarray[idx]; idx++) { if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS && keyarray[idx]->subkeys && keyarray[idx]->subkeys->fpr && *keyarray[idx]->subkeys->fpr) buflen += strlen (keyarray[idx]->subkeys->fpr) + 1; } /* Allocate a bufer with extra space for the trailing Nul introduced by the use of stpcpy. */ buffer = malloc (buflen+1); if (!buffer) return gpg_error_from_syserror (); for (idx=0, p = buffer; keyarray[idx]; idx++) { if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS && keyarray[idx]->subkeys && keyarray[idx]->subkeys->fpr && *keyarray[idx]->subkeys->fpr) p = stpcpy (stpcpy (p, keyarray[idx]->subkeys->fpr), "\n"); } err = gpgme_data_new_from_mem (&gpgsm->input_helper_data, buffer, buflen, 0); if (err) { free (buffer); return err; } gpgsm->input_helper_memory = buffer; gpgsm->input_cb.data = gpgsm->input_helper_data; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) { gpgme_data_release (gpgsm->input_helper_data); gpgsm->input_helper_data = NULL; free (gpgsm->input_helper_memory); gpgsm->input_helper_memory = NULL; return err; } gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; return start (gpgsm, "IMPORT --re-import"); } else if (dataenc == GPGME_DATA_ENCODING_URL || dataenc == GPGME_DATA_ENCODING_URL0 || dataenc == GPGME_DATA_ENCODING_URLESC) { return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } else { gpgsm->input_cb.data = keydata; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; return start (gpgsm, "IMPORT"); } }