static gpgme_error_t gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd, engine_status_handler_t status_fnc, void *status_fnc_value) { gpg_error_t err; char *line; size_t linelen; err = assuan_write_line (ctx, cmd); if (err) return err; do { err = assuan_read_line (ctx, &line, &linelen); if (err) return err; if (*line == '#' || !linelen) continue; if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) return 0; else if (linelen >= 4 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && line[3] == ' ') err = atoi (&line[4]); else if (linelen >= 2 && line[0] == 'S' && line[1] == ' ') { 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 (r >= 0 && status_fnc) err = status_fnc (status_fnc_value, r, rest); else err = gpg_error (GPG_ERR_GENERAL); } else err = gpg_error (GPG_ERR_GENERAL); } while (!err); return err; }
static gpg_error_t cancel_inquire (ctrl_t ctrl, gpg_error_t rc) { gpg_error_t oldrc = rc; /* The inquire callback was called and transact returned a cancel error. We assume that the inquired process sent a CANCEL. The passthrough code is not able to pass on the CANCEL and thus scdaemon would stuck on this. As a workaround we send a CANCEL now. */ rc = assuan_write_line (ctrl->scd_local->ctx, "CAN"); if (!rc) { char *line; size_t len; rc = assuan_read_line (ctrl->scd_local->ctx, &line, &len); if (!rc) rc = oldrc; } return rc; }
static gpgme_error_t status_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value; gpgme_error_t err = 0; char *line; size_t linelen; do { err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen); if (err) { /* Try our best to terminate the connection friendly. */ /* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */ TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: error from assuan (%d) getting status line : %s", fd, err, gpg_strerror (err)); } else if (linelen >= 3 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && (line[3] == '\0' || line[3] == ' ')) { if (line[3] == ' ') err = atoi (&line[4]); if (! err) err = gpg_error (GPG_ERR_GENERAL); TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: ERR line - mapped to: %s", fd, err ? gpg_strerror (err) : "ok"); /* Try our best to terminate the connection friendly. */ /* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */ } else if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) { if (gpgsm->status.fnc) err = gpgsm->status.fnc (gpgsm->status.fnc_value, GPGME_STATUS_EOF, ""); if (!err && gpgsm->colon.fnc && gpgsm->colon.any) { /* We must tell a colon function about the EOF. We do this only when we have seen any data lines. Note that this inlined use of colon data lines will eventually be changed into using a regular data channel. */ gpgsm->colon.any = 0; err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL); } TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: OK line - final status: %s", fd, err ? gpg_strerror (err) : "ok"); _gpgme_io_close (gpgsm->status_cb.fd); return err; } else if (linelen > 2 && line[0] == 'D' && line[1] == ' ' && gpgsm->colon.fnc) { /* We are using the colon handler even for plain inline data - strange name for that function but for historic reasons we keep it. */ /* FIXME We can't use this for binary data because we assume this is a string. For the current usage of colon output it is correct. */ char *src = line + 2; char *end = line + linelen; char *dst; char **aline = &gpgsm->colon.attic.line; int *alinelen = &gpgsm->colon.attic.linelen; if (gpgsm->colon.attic.linesize < *alinelen + linelen + 1) { char *newline = realloc (*aline, *alinelen + linelen + 1); if (!newline) err = gpg_error_from_syserror (); else { *aline = newline; gpgsm->colon.attic.linesize += linelen + 1; } } if (!err) { dst = *aline + *alinelen; while (!err && src < end) { if (*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst = _gpgme_hextobyte (src); (*alinelen)++; src += 2; } else { *dst = *src++; (*alinelen)++; } if (*dst == '\n') { /* Terminate the pending line, pass it to the colon handler and reset it. */ gpgsm->colon.any = 1; if (*alinelen > 1 && *(dst - 1) == '\r') dst--; *dst = '\0'; /* FIXME How should we handle the return code? */ err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline); if (!err) { dst = *aline; *alinelen = 0; } } else dst++; } } TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: D line; final status: %s", fd, err? gpg_strerror (err):"ok"); } else if (linelen > 2 && line[0] == 'D' && line[1] == ' ' && gpgsm->inline_data) { char *src = line + 2; char *end = line + linelen; char *dst = src; ssize_t nwritten; linelen = 0; while (src < end) { if (*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst++ = _gpgme_hextobyte (src); src += 2; } else *dst++ = *src++; linelen++; } src = line + 2; while (linelen > 0) { nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen); if (!nwritten || (nwritten < 0 && errno != EINTR) || nwritten > linelen) { err = gpg_error_from_syserror (); break; } src += nwritten; linelen -= nwritten; } TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: D inlinedata; final status: %s", fd, err? gpg_strerror (err):"ok"); } else if (linelen > 2 && line[0] == 'S' && line[1] == ' ') { 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 (r >= 0) { if (gpgsm->status.fnc) err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest); } else fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest); TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: S line (%s) - final status: %s", fd, line+2, err? gpg_strerror (err):"ok"); } else if (linelen >= 7 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q' && line[3] == 'U' && line[4] == 'I' && line[5] == 'R' && line[6] == 'E' && (line[7] == '\0' || line[7] == ' ')) { char *keyword = line+7; while (*keyword == ' ') keyword++;; default_inq_cb (gpgsm, keyword); assuan_write_line (gpgsm->assuan_ctx, "END"); } } while (!err && assuan_pending_line (gpgsm->assuan_ctx)); return err; }
static gpgme_error_t status_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_g13_t g13 = (engine_g13_t) data->handler_value; gpgme_error_t err = 0; char *line; size_t linelen; do { err = assuan_read_line (g13->assuan_ctx, &line, &linelen); if (err) { /* Try our best to terminate the connection friendly. */ /* assuan_write_line (g13->assuan_ctx, "BYE"); */ TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13, "fd 0x%x: error reading assuan line: %s", fd, gpg_strerror (err)); } else if (linelen >= 3 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && (line[3] == '\0' || line[3] == ' ')) { if (line[3] == ' ') err = atoi (&line[4]); if (! err) err = gpg_error (GPG_ERR_GENERAL); TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13, "fd 0x%x: ERR line: %s", fd, err ? gpg_strerror (err) : "ok"); /* Command execution errors are not fatal, as we use a session based protocol. */ data->op_err = err; /* The caller will do the rest (namely, call cancel_op, which closes status_fd). */ return 0; } else if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) { TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13, "fd 0x%x: OK line", fd); _gpgme_io_close (g13->status_cb.fd); return 0; } else if (linelen > 2 && line[0] == 'D' && line[1] == ' ') { /* We are using the colon handler even for plain inline data - strange name for that function but for historic reasons we keep it. */ /* FIXME We can't use this for binary data because we assume this is a string. For the current usage of colon output it is correct. */ char *src = line + 2; char *end = line + linelen; char *dst = src; linelen = 0; while (src < end) { if (*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst++ = _gpgme_hextobyte (src); src += 2; } else *dst++ = *src++; linelen++; } src = line + 2; if (linelen && g13->user.data_cb) err = g13->user.data_cb (g13->user.data_cb_value, src, linelen); else err = 0; TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13, "fd 0x%x: D inlinedata; status from cb: %s", fd, (g13->user.data_cb ? (err? gpg_strerror (err):"ok"):"no callback")); } else if (linelen > 2 && line[0] == 'S' && line[1] == ' ') { char *src; char *args; src = line + 2; while (*src == ' ') src++; args = strchr (line + 2, ' '); if (!args) args = line + linelen; /* set to an empty string */ else *(args++) = 0; while (*args == ' ') args++; if (g13->user.status_cb) err = g13->user.status_cb (g13->user.status_cb_value, src, args); else err = 0; TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13, "fd 0x%x: S line (%s) - status from cb: %s", fd, line+2, (g13->user.status_cb ? (err? gpg_strerror (err):"ok"):"no callback")); } else if (linelen >= 7 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q' && line[3] == 'U' && line[4] == 'I' && line[5] == 'R' && line[6] == 'E' && (line[7] == '\0' || line[7] == ' ')) { char *src; char *args; for (src=line+7; *src == ' '; src++) ; args = strchr (src, ' '); if (!args) args = line + linelen; /* Let it point to an empty string. */ else *(args++) = 0; while (*args == ' ') args++; err = default_inq_cb (g13, src, args); if (!err) { /* Flush and send END. */ err = assuan_send_data (g13->assuan_ctx, NULL, 0); } else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED) { /* Flush and send CANcel. */ err = assuan_send_data (g13->assuan_ctx, NULL, 1); } assuan_write_line (g13->assuan_ctx, "END"); } } while (!err && assuan_pending_line (g13->assuan_ctx)); return err; }
/* Run a quality inquiry for PASSPHRASE of LENGTH. (We need LENGTH because not all backends might be able to return a proper C-string.). Returns: A value between -100 and 100 to give an estimate of the passphrase's quality. Negative values are use if the caller won't even accept that passphrase. Note that we expect just one data line which should not be escaped in any represent a numeric signed decimal value. Extra data is currently ignored but should not be send at all. */ int pinentry_inq_quality (pinentry_t pin, const char *passphrase, size_t length) { ASSUAN_CONTEXT ctx = pin->ctx_assuan; const char prefix[] = "INQUIRE QUALITY "; char *command; char *line; size_t linelen; int gotvalue = 0; int value = 0; int rc; if (!ctx) return 0; /* Can't run the callback. */ if (length > 300) length = 300; /* Limit so that it definitely fits into an Assuan line. */ command = secmem_malloc (strlen (prefix) + 3*length + 1); if (!command) return 0; strcpy (command, prefix); copy_and_escape (command + strlen(command), passphrase, length); rc = assuan_write_line (ctx, command); secmem_free (command); if (rc) { fprintf (stderr, "ASSUAN WRITE LINE failed: rc=%d\n", rc); return 0; } for (;;) { do { rc = assuan_read_line (ctx, &line, &linelen); if (rc) { fprintf (stderr, "ASSUAN READ LINE failed: rc=%d\n", rc); return 0; } } while (*line == '#' || !linelen); if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D' && (!line[3] || line[3] == ' ')) break; /* END command received*/ if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N' && (!line[3] || line[3] == ' ')) break; /* CAN command received*/ if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && (!line[3] || line[3] == ' ')) break; /* ERR command received*/ if (line[0] != 'D' || line[1] != ' ' || linelen < 3 || gotvalue) continue; gotvalue = 1; value = atoi (line+2); } if (value < -100) value = -100; else if (value > 100) value = 100; return value; }
static gpgme_error_t status_handler(void *opaque, int fd) { gpg_error_t assuan_err; gpgme_error_t err = 0; engine_gpgsm_t gpgsm = opaque; char *line; size_t linelen; do { assuan_err = assuan_read_line(gpgsm->assuan_ctx, &line, &linelen); if(assuan_err) { /* Try our best to terminate the connection friendly. */ /* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */ err = map_assuan_error(assuan_err); DEBUG3("fd %d: error from assuan (%d) getting status line : %s \n", fd, assuan_err, gpg_strerror(err)); } else if(linelen >= 3 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && (line[3] == '\0' || line[3] == ' ')) { if(line[3] == ' ') err = map_assuan_error(atoi(&line[4])); else err = gpg_error(GPG_ERR_GENERAL); DEBUG2("fd %d: ERR line - mapped to: %s\n", fd, err ? gpg_strerror(err) : "ok"); } else if(linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) { if(gpgsm->status.fnc) err = gpgsm->status.fnc(gpgsm->status.fnc_value, GPGME_STATUS_EOF, ""); if(!err && gpgsm->colon.fnc && gpgsm->colon.any) { /* We must tell a colon function about the EOF. We do this only when we have seen any data lines. Note that this inlined use of colon data lines will eventually be changed into using a regular data channel. */ gpgsm->colon.any = 0; err = gpgsm->colon.fnc(gpgsm->colon.fnc_value, NULL); } _gpgme_io_close(gpgsm->status_cb.fd); DEBUG2("fd %d: OK line - final status: %s\n", fd, err ? gpg_strerror(err) : "ok"); return err; } else if(linelen > 2 && line[0] == 'D' && line[1] == ' ' && gpgsm->colon.fnc) { /* We are using the colon handler even for plain inline data - strange name for that function but for historic reasons we keep it. */ /* FIXME We can't use this for binary data because we assume this is a string. For the current usage of colon output it is correct. */ char *src = line + 2; char *end = line + linelen; char *dst; char **aline = &gpgsm->colon.attic.line; int *alinelen = &gpgsm->colon.attic.linelen; if(gpgsm->colon.attic.linesize < *alinelen + linelen + 1) { char *newline = realloc(*aline, *alinelen + linelen + 1); if(!newline) err = gpg_error_from_errno(errno); else { *aline = newline; gpgsm->colon.attic.linesize += linelen + 1; } } if(!err) { dst = *aline + *alinelen; while(!err && src < end) { if(*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst = _gpgme_hextobyte(src); (*alinelen)++; src += 2; } else { *dst = *src++; (*alinelen)++; } if(*dst == '\n') { /* Terminate the pending line, pass it to the colon handler and reset it. */ gpgsm->colon.any = 1; if(*alinelen > 1 && *(dst - 1) == '\r') dst--; *dst = '\0'; /* FIXME How should we handle the return code? */ err = gpgsm->colon.fnc(gpgsm->colon.fnc_value, *aline); if(!err) { dst = *aline; *alinelen = 0; } } else dst++; } } DEBUG2("fd %d: D line; final status: %s\n", fd, err ? gpg_strerror(err) : "ok"); } else if(linelen > 2 && line[0] == 'S' && line[1] == ' ') { 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 = parse_status(line + 2); if(r >= 0) { if(gpgsm->status.fnc) err = gpgsm->status.fnc(gpgsm->status.fnc_value, r, rest); } else fprintf(stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest); DEBUG3("fd %d: S line (%s) - final status: %s\n", fd, line + 2, err ? gpg_strerror(err) : "ok"); } } while(!err && assuan_pending_line(gpgsm->assuan_ctx)); return err; }