summaryrefslogtreecommitdiffstats
path: root/src/engine-gpgsm.c
diff options
context:
space:
mode:
authorIngo Klöcker <[email protected]>2026-03-24 15:56:39 +0100
committerIngo Klöcker <[email protected]>2026-03-24 15:56:39 +0100
commit775e1eb923fa0f2f66363d5329acfebc13c864b3 (patch)
treeca20b80bcc0f43fb9938f8bee95fffa6baebe16d /src/engine-gpgsm.c
parentFix passphrase cancel handling. (diff)
downloadgpgme-775e1eb923fa0f2f66363d5329acfebc13c864b3.tar.gz
gpgme-775e1eb923fa0f2f66363d5329acfebc13c864b3.zip
gpgsm: Read all pending lines before waiting for more data
* src/engine-gpgsm.c (gpgsm_assuan_simple_command): Add a loop to read all pending lines. -- This fixes the problem that gpgme waited indefinitely for the completion of the "simple command" if the reply consisted of more than one line. For example, if an invalid recipient is sent to gpgsm then gpgsm replies something like S INV_RECP 10 798413A50E4C66292914361E8358AC45668F412A<LF> ERR 50331746 Not trusted <GpgSM><LF> gpgsm_assuan_simple_command read the first line and then waited forever for more data sent by gpgsm. GnuPG-bug-id: 8187
Diffstat (limited to 'src/engine-gpgsm.c')
-rw-r--r--src/engine-gpgsm.c142
1 files changed, 74 insertions, 68 deletions
diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c
index 206d3e4b..302c1a8e 100644
--- a/src/engine-gpgsm.c
+++ b/src/engine-gpgsm.c
@@ -696,6 +696,7 @@ gpgsm_assuan_simple_command (engine_gpgsm_t gpgsm, const char *cmd,
struct io_select_fd_s fds[2];
struct io_cb_data iocb_data;
int nfds = 0;
+ int done = 0;
iocb_data.handler_value = gpgsm->diag_cb.data;
iocb_data.op_err = 0;
@@ -738,88 +739,93 @@ gpgsm_assuan_simple_command (engine_gpgsm_t gpgsm, const char *cmd,
if (gpgsm->status_cb.fd != -1 && !fds[0].signaled)
continue;
- err = assuan_read_line (ctx, &line, &linelen);
- if (err)
+ do
{
- TRACE (DEBUG_CTX, "gpgsm_assuan_simple_command", gpgsm,
+ err = assuan_read_line (ctx, &line, &linelen);
+ if (err)
+ {
+ TRACE (DEBUG_CTX, "gpgsm_assuan_simple_command", gpgsm,
"error from assuan (%d) getting line: %s",
- err, gpg_strerror (err));
- break;
- }
+ err, gpg_strerror (err));
+ break;
+ }
- if (*line == '#' || !linelen)
- continue;
+ if (*line == '#' || !linelen)
+ continue;
- if (linelen >= 2
- && line[0] == 'O' && line[1] == 'K'
- && (line[2] == '\0' || line[2] == ' '))
- {
- TRACE (DEBUG_CTX, "gpgsm_assuan_simple_command", gpgsm,
- "OK line seen");
- break;
- }
- else if (linelen >= 4
- && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
- && line[3] == ' ')
- {
- err = atoi (&line[4]);
- TRACE (DEBUG_CTX, "gpgsm_assuan_simple_command", gpgsm,
- "ERR line seen: err=%s, cb_err=%s",
- gpg_strerror (err), cb_err? gpg_strerror (cb_err):"none");
- /* We prefer a callback generated error because that one is
- more related to gpgme and thus probably more important
- than the error returned by the engine. */
- if (cb_err)
+ if (linelen >= 2
+ && line[0] == 'O' && line[1] == 'K'
+ && (line[2] == '\0' || line[2] == ' '))
{
- err = cb_err;
- cb_err = 0;
+ TRACE (DEBUG_CTX, "gpgsm_assuan_simple_command", gpgsm,
+ "OK line seen");
+ done = 1;
+ break;
}
- }
- else if (linelen >= 2
- && line[0] == 'S' && line[1] == ' ')
- {
- /* After an error from a status callback we skip all further
- status lines. */
- TRACE (DEBUG_CTX, "gpgsm_assuan_simple_command", gpgsm,
- "S line seen: line='%s'", line);
- if (!cb_err)
+ else if (linelen >= 4
+ && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
+ && line[3] == ' ')
{
- char *rest;
- gpgme_status_code_t r;
-
- rest = strchr (line + 2, ' ');
- if (!rest)
- rest = line + linelen; /* set to an empty string */
- else
- *(rest++) = 0;
-
- r = _gpgme_parse_status (line + 2);
- if (gpgsm->status.mon_cb && r != GPGME_STATUS_PROGRESS)
+ err = atoi (&line[4]);
+ TRACE (DEBUG_CTX, "gpgsm_assuan_simple_command", gpgsm,
+ "ERR line seen: err=%s, cb_err=%s",
+ gpg_strerror (err), cb_err? gpg_strerror (cb_err):"none");
+ /* We prefer a callback generated error because that one is
+ more related to gpgme and thus probably more important
+ than the error returned by the engine. */
+ if (cb_err)
{
- /* Note that we call the monitor even if we do
- * not know the status code (r < 0). */
- cb_err = gpgsm->status.mon_cb (gpgsm->status.mon_cb_value,
- line + 2, rest);
+ err = cb_err;
+ cb_err = 0;
}
+ }
+ else if (linelen >= 2
+ && line[0] == 'S' && line[1] == ' ')
+ {
+ /* After an error from a status callback we skip all further
+ status lines. */
+ TRACE (DEBUG_CTX, "gpgsm_assuan_simple_command", gpgsm,
+ "S line seen: line='%s'", line);
+ if (!cb_err)
+ {
+ char *rest;
+ gpgme_status_code_t r;
+
+ rest = strchr (line + 2, ' ');
+ if (!rest)
+ rest = line + linelen; /* set to an empty string */
+ else
+ *(rest++) = 0;
- if (r >= 0 && status_fnc && !cb_err)
- cb_err = status_fnc (status_fnc_value, r, rest);
+ r = _gpgme_parse_status (line + 2);
+ if (gpgsm->status.mon_cb && r != GPGME_STATUS_PROGRESS)
+ {
+ /* Note that we call the monitor even if we do
+ * not know the status code (r < 0). */
+ cb_err = gpgsm->status.mon_cb (gpgsm->status.mon_cb_value,
+ line + 2, rest);
+ }
+
+ if (r >= 0 && status_fnc && !cb_err)
+ cb_err = status_fnc (status_fnc_value, r, rest);
+ TRACE (DEBUG_CTX, "gpgsm_assuan_simple_command", gpgsm,
+ "S line yields cb_err=%s", gpg_strerror (cb_err));
+ }
+ }
+ else
+ {
+ /* Invalid line or INQUIRY. We can't do anything else than
+ to stop. As with ERR we prefer a status callback
+ generated error code, though. */
TRACE (DEBUG_CTX, "gpgsm_assuan_simple_command", gpgsm,
- "S line yields cb_err=%s", gpg_strerror (cb_err));
+ "Invalid line seen: %s", line);
+ err = cb_err ? cb_err : gpg_error (GPG_ERR_GENERAL);
+ cb_err = 0;
}
- }
- else
- {
- /* Invalid line or INQUIRY. We can't do anything else than
- to stop. As with ERR we prefer a status callback
- generated error code, though. */
- TRACE (DEBUG_CTX, "gpgsm_assuan_simple_command", gpgsm,
- "Invalid line seen: %s", line);
- err = cb_err ? cb_err : gpg_error (GPG_ERR_GENERAL);
- cb_err = 0;
}
+ while (!err && assuan_pending_line (ctx));
}
- while (!err);
+ while (!err && !done);
/* We only want the first error from the status handler, thus we
* take the one saved in CB_ERR. */