diff --git a/src/assuan-inquire.c b/src/assuan-inquire.c index 23cb48b..e1b3ac9 100644 --- a/src/assuan-inquire.c +++ b/src/assuan-inquire.c @@ -1,421 +1,421 @@ /* assuan-inquire.c - handle inquire stuff * Copyright (C) 2001-2003, 2005, 2007, 2009 Free Software Foundation, Inc. * * This file is part of Assuan. * * Assuan is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Assuan is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "assuan-defs.h" #define digitp(a) ((a) >= '0' && (a) <= '9') #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) struct membuf { size_t len; size_t size; char *buf; int out_of_core; int too_large; size_t maxlen; }; /* A simple implementation of a dynamic buffer. Use init_membuf() to create a buffer, put_membuf to append bytes and get_membuf to release and return the buffer. Allocation errors are detected but only returned at the final get_membuf(), this helps not to clutter the code with out of core checks. */ static void init_membuf (assuan_context_t ctx, struct membuf *mb, int initiallen, size_t maxlen) { mb->len = 0; mb->size = initiallen; mb->out_of_core = 0; mb->too_large = 0; mb->maxlen = maxlen; /* we need to allocate one byte more for get_membuf */ mb->buf = _assuan_malloc (ctx, initiallen + 1); if (!mb->buf) mb->out_of_core = 1; } static void put_membuf (assuan_context_t ctx, struct membuf *mb, const void *buf, size_t len) { if (mb->out_of_core || mb->too_large) return; if (mb->maxlen && mb->len + len > mb->maxlen) { mb->too_large = 1; return; } if (mb->len + len >= mb->size) { char *p; mb->size += len + 1024; /* we need to allocate one byte more for get_membuf */ p = _assuan_realloc (ctx, mb->buf, mb->size + 1); if (!p) { mb->out_of_core = 1; return; } mb->buf = p; } memcpy (mb->buf + mb->len, buf, len); mb->len += len; } static void * get_membuf (assuan_context_t ctx, struct membuf *mb, size_t *len) { char *p; if (mb->out_of_core || mb->too_large) { _assuan_free (ctx, mb->buf); mb->buf = NULL; return NULL; } mb->buf[mb->len] = 0; /* there is enough space for the hidden eos */ p = mb->buf; *len = mb->len; mb->buf = NULL; mb->out_of_core = 1; /* don't allow a reuse */ return p; } static void free_membuf (assuan_context_t ctx, struct membuf *mb) { _assuan_free (ctx, mb->buf); mb->buf = NULL; } /** * assuan_inquire: * @ctx: An assuan context * @keyword: The keyword used for the inquire * @r_buffer: Returns an allocated buffer * @r_length: Returns the length of this buffer * @maxlen: If not 0, the size limit of the inquired data. * * A server may use this to send an inquire. r_buffer, r_length and * maxlen may all be NULL/0 to indicate that no real data is expected. * The returned buffer is guaranteed to have an extra 0-byte after the * length. Thus it can be used as a string if embedded 0 bytes are * not an issue. * * Return value: 0 on success or an ASSUAN error code **/ gpg_error_t assuan_inquire (assuan_context_t ctx, const char *keyword, unsigned char **r_buffer, size_t *r_length, size_t maxlen) { gpg_error_t rc; struct membuf mb; char cmdbuf[LINELENGTH-10]; /* (10 = strlen ("INQUIRE ")+CR,LF) */ unsigned char *line, *p; int linelen; int nodataexpected; if (r_buffer) *r_buffer = NULL; if (r_length) *r_length = 0; if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf))) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); nodataexpected = !r_buffer && !r_length && !maxlen; if (!nodataexpected && (!r_buffer || !r_length)) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!ctx->flags.is_server) return _assuan_error (ctx, GPG_ERR_ASS_NOT_A_SERVER); if (ctx->flags.in_inquire) return _assuan_error (ctx, GPG_ERR_ASS_NESTED_COMMANDS); ctx->flags.in_inquire = 1; if (nodataexpected) memset (&mb, 0, sizeof mb); /* avoid compiler warnings */ else init_membuf (ctx, &mb, maxlen? maxlen:1024, maxlen); strcpy (stpcpy (cmdbuf, "INQUIRE "), keyword); rc = assuan_write_line (ctx, cmdbuf); if (rc) goto out; for (;;) { do { do rc = _assuan_read_line (ctx); while (_assuan_error_is_eagain (ctx, rc)); if (rc) goto out; line = (unsigned char *) ctx->inbound.line; linelen = ctx->inbound.linelen; } while (*line == '#' || !linelen); /* Note: As a convenience for manual testing we allow case insensitive keywords. */ if ((line[0] == 'E'||line[0] == 'e') && (line[1] == 'N' || line[1] == 'n') && (line[2] == 'D' || line[2] == 'd') && (!line[3] || line[3] == ' ')) break; /* END command received*/ if ((line[0] == 'C' || line[0] == 'c') && (line[1] == 'A' || line[1] == 'a') && (line[2] == 'N' || line[2] == 'n')) { rc = _assuan_error (ctx, GPG_ERR_ASS_CANCELED); goto out; } if ((line[0] != 'D' && line[0] != 'd') || line[1] != ' ' || nodataexpected) { rc = _assuan_error (ctx, GPG_ERR_ASS_UNEXPECTED_CMD); goto out; } if (linelen < 3) continue; line += 2; linelen -= 2; if (mb.too_large) continue; /* Need to read up the remaining data. */ p = line; while (linelen) { for (;linelen && *p != '%'; linelen--, p++) ; put_membuf (ctx, &mb, line, p-line); if (linelen > 2) { /* handle escaping */ unsigned char tmp[1]; p++; *tmp = xtoi_2 (p); p += 2; linelen -= 3; put_membuf (ctx, &mb, tmp, 1); } line = p; } } if (!nodataexpected) { if (mb.too_large) rc = _assuan_error (ctx, GPG_ERR_ASS_TOO_MUCH_DATA); else { *r_buffer = get_membuf (ctx, &mb, r_length); if (!*r_buffer) rc = _assuan_error (ctx, gpg_err_code_from_syserror ()); } } out: if (!nodataexpected) { - if (ctx->flags.confidential && mb->buf) + if (ctx->flags.confidential && mb.buf) wipememory (mb.buf, mb.len); free_membuf (ctx, &mb); } if (ctx->flags.confidential) wipememory (ctx->inbound.line, LINELENGTH); ctx->flags.in_inquire = 0; return rc; } void _assuan_inquire_release (assuan_context_t ctx) { if (ctx->flags.in_inquire) { if (ctx->inquire_membuf) { free_membuf (ctx, ctx->inquire_membuf); free (ctx->inquire_membuf); } ctx->flags.in_inquire = 0; } } gpg_error_t _assuan_inquire_ext_cb (assuan_context_t ctx) { gpg_error_t rc; unsigned char *line; int linelen; struct membuf *mb; unsigned char *p; line = (unsigned char *) ctx->inbound.line; linelen = ctx->inbound.linelen; mb = ctx->inquire_membuf; if ((line[0] == 'C' || line[0] == 'c') && (line[1] == 'A' || line[1] == 'a') && (line[2] == 'N' || line[2] == 'n')) { rc = _assuan_error (ctx, GPG_ERR_ASS_CANCELED); goto out; } if ((line[0] == 'E'||line[0] == 'e') && (line[1] == 'N' || line[1] == 'n') && (line[2] == 'D' || line[2] == 'd') && (!line[3] || line[3] == ' ')) { rc = 0; goto out; } if ((line[0] != 'D' && line[0] != 'd') || line[1] != ' ' || mb == NULL) { rc = _assuan_error (ctx, GPG_ERR_ASS_UNEXPECTED_CMD); goto out; } if (linelen < 3) return 0; line += 2; linelen -= 2; p = line; while (linelen) { for (;linelen && *p != '%'; linelen--, p++) ; put_membuf (ctx, mb, line, p-line); if (linelen > 2) { /* handle escaping */ unsigned char tmp[1]; p++; *tmp = xtoi_2 (p); p += 2; linelen -= 3; put_membuf (ctx, mb, tmp, 1); } line = p; } if (mb->too_large) { rc = _assuan_error (ctx, GPG_ERR_ASS_TOO_MUCH_DATA); goto out; } return 0; out: { size_t buf_len = 0; unsigned char *buf = NULL; if (mb) { buf = get_membuf (ctx, mb, &buf_len); if (!buf) rc = _assuan_error (ctx, gpg_err_code_from_syserror ()); free_membuf (ctx, mb); free (mb); ctx->inquire_membuf = NULL; } ctx->flags.in_inquire = 0; rc = (ctx->inquire_cb) (ctx->inquire_cb_data, rc, buf, buf_len); } return rc; } /** * assuan_inquire_ext: * @ctx: An assuan context * @keyword: The keyword used for the inquire * @maxlen: If not 0, the size limit of the inquired data. * @cb: A callback handler which is invoked after the operation completed. * @cb_data: A user-provided value passed to the callback handler. * * A server may use this to send an inquire. R_BUFFER, R_LENGTH and * MAXLEN may all be NULL/0 to indicate that no real data is expected. * * Return value: 0 on success or an ASSUAN error code **/ gpg_error_t assuan_inquire_ext (assuan_context_t ctx, const char *keyword, size_t maxlen, gpg_error_t (*cb) (void *cb_data, gpg_error_t rc, unsigned char *buf, size_t len), void *cb_data) { gpg_error_t rc; struct membuf *mb = NULL; char cmdbuf[LINELENGTH-10]; /* (10 = strlen ("INQUIRE ")+CR,LF) */ if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf))) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!ctx->flags.is_server) return _assuan_error (ctx, GPG_ERR_ASS_NOT_A_SERVER); if (ctx->flags.in_inquire) return _assuan_error (ctx, GPG_ERR_ASS_NESTED_COMMANDS); mb = malloc (sizeof (struct membuf)); if (!mb) return _assuan_error (ctx, gpg_err_code_from_syserror ()); init_membuf (ctx, mb, maxlen ? maxlen : 1024, maxlen); strcpy (stpcpy (cmdbuf, "INQUIRE "), keyword); rc = assuan_write_line (ctx, cmdbuf); if (rc) { free_membuf (ctx, mb); free (mb); return rc; } ctx->flags.in_inquire = 1; /* Set up the continuation. */ ctx->inquire_cb = cb; ctx->inquire_cb_data = cb_data; ctx->inquire_membuf = mb; return 0; }