diff --git a/src/data-compat.c b/src/data-compat.c index 9091b968..64ed2d28 100644 --- a/src/data-compat.c +++ b/src/data-compat.c @@ -1,233 +1,234 @@ /* data-compat.c - Compatibility interfaces for data objects. * Copyright (C) 2002, 2003, 2004, 2007 g10 Code GmbH * * This file is part of GPGME. * * GPGME 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. * * GPGME 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-or-later */ #if HAVE_CONFIG_H #include #endif #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #include #include "data.h" #include "util.h" #include "debug.h" /* Create a new data buffer filled with LENGTH bytes starting from OFFSET within the file FNAME or stream STREAM (exactly one must be non-zero). */ gpgme_error_t gpgme_data_new_from_filepart (gpgme_data_t *r_dh, const char *fname, FILE *stream, gpgme_off_t offset, size_t length) { gpgme_error_t err; char *buf = NULL; int res; TRACE_BEG (DEBUG_DATA, "gpgme_data_new_from_filepart", r_dh, "file_name=%s, stream=%p, offset=%lli, length=%zu", fname, stream, (long long int)offset, length); if (stream && fname) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (fname) stream = fopen (fname, "rb"); if (!stream) return TRACE_ERR (gpg_error_from_syserror ()); #ifdef HAVE_FSEEKO res = fseeko (stream, offset, SEEK_SET); #else /* FIXME: Check for overflow, or at least bail at compilation. */ res = fseek (stream, offset, SEEK_SET); #endif if (res) { int saved_err = gpg_error_from_syserror (); if (fname) fclose (stream); return TRACE_ERR (saved_err); } buf = malloc (length); if (!buf) { int saved_err = gpg_error_from_syserror (); if (fname) fclose (stream); return TRACE_ERR (saved_err); } while (fread (buf, length, 1, stream) < 1 && ferror (stream) && errno == EINTR); if (ferror (stream)) { int saved_err = gpg_error_from_syserror (); if (buf) free (buf); if (fname) fclose (stream); return TRACE_ERR (saved_err); } if (fname) fclose (stream); err = gpgme_data_new (r_dh); if (err) { if (buf) free (buf); return err; } (*r_dh)->data.mem.buffer = buf; (*r_dh)->data.mem.size = length; (*r_dh)->data.mem.length = length; - return TRACE_SUC ("r_dh=%p", *r_dh); + TRACE_SUC ("r_dh=%p", *r_dh); + return 0; } /* Create a new data buffer filled with the content of file FNAME. COPY must be non-zero (delayed reads are not supported yet). */ gpgme_error_t gpgme_data_new_from_file (gpgme_data_t *r_dh, const char *fname, int copy) { gpgme_error_t err; struct stat statbuf; TRACE_BEG (DEBUG_DATA, "gpgme_data_new_from_file", r_dh, "file_name=%s, copy=%i (%s)", fname, copy, copy ? "yes" : "no"); if (!fname || !copy) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (stat (fname, &statbuf) < 0) return TRACE_ERR (gpg_error_from_syserror ()); err = gpgme_data_new_from_filepart (r_dh, fname, NULL, 0, statbuf.st_size); return TRACE_ERR (err); } static int gpgme_error_to_errno (gpgme_error_t err) { int res = gpg_err_code_to_errno (gpg_err_code (err)); if (!err) { switch (gpg_err_code (err)) { case GPG_ERR_EOF: res = 0; break; case GPG_ERR_INV_VALUE: res = EINVAL; break; case GPG_ERR_NOT_SUPPORTED: res = ENOSYS; break; default: /* FIXME: Yeah, well. */ res = EINVAL; break; } } TRACE (DEBUG_DATA, "gpgme:gpgme_error_to_errno", 0, "mapping %s <%s> to: %s", gpgme_strerror (err), gpgme_strsource (err), strerror (res)); gpg_err_set_errno (res); return res ? -1 : 0; } static gpgme_ssize_t old_user_read (gpgme_data_t dh, void *buffer, size_t size) { gpgme_error_t err; size_t amt; TRACE_BEG (DEBUG_DATA, "gpgme:old_user_read", dh, "buffer=%p, size=%zu", buffer, size); err = (*dh->data.old_user.cb) (dh->data.old_user.handle, buffer, size, &amt); if (err) return TRACE_SYSRES (gpgme_error_to_errno (err)); return TRACE_SYSRES ((int)amt); } static gpgme_off_t old_user_seek (gpgme_data_t dh, gpgme_off_t offset, int whence) { gpgme_error_t err; TRACE_BEG (DEBUG_DATA, "gpgme:old_user_seek", dh, "offset=%llu, whence=%i", (long long int)offset, whence); if (whence != SEEK_SET || offset) { gpg_err_set_errno (EINVAL); return TRACE_SYSRES (-1); } err = (*dh->data.old_user.cb) (dh->data.old_user.handle, NULL, 0, NULL); if (err) return TRACE_SYSRES (gpgme_error_to_errno (err)); return TRACE_SYSRES (0); } static struct _gpgme_data_cbs old_user_cbs = { old_user_read, NULL, old_user_seek, NULL }; /* Create a new data buffer which retrieves the data from the callback function READ_CB. */ gpgme_error_t gpgme_data_new_with_read_cb (gpgme_data_t *r_dh, int (*read_cb) (void *, char *, size_t, size_t *), void *read_cb_value) { gpgme_error_t err; TRACE_BEG (DEBUG_DATA, "gpgme_data_new_with_read_cb", r_dh, "read_cb=%p/%p", read_cb, read_cb_value); err = _gpgme_data_new (r_dh, &old_user_cbs); if (err) return TRACE_ERR (err); (*r_dh)->data.old_user.cb = read_cb; (*r_dh)->data.old_user.handle = read_cb_value; return TRACE_ERR (0); } diff --git a/src/data-estream.c b/src/data-estream.c index 1f1a64eb..1855e74e 100644 --- a/src/data-estream.c +++ b/src/data-estream.c @@ -1,99 +1,100 @@ /* data-stream.c - A stream based data object. * Copyright (C) 2002, 2004, 2018 g10 Code GmbH * * This file is part of GPGME. * * GPGME 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. * * GPGME 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+ */ #if HAVE_CONFIG_H #include #endif #include #ifdef HAVE_SYS_TYPES_H # include #endif #include "debug.h" #include "data.h" static gpgme_ssize_t stream_es_read (gpgme_data_t dh, void *buffer, size_t size) { size_t amt = gpgrt_fread (buffer, 1, size, dh->data.e_stream); if (amt > 0) return amt; return gpgrt_ferror (dh->data.e_stream) ? -1 : 0; } static gpgme_ssize_t stream_es_write (gpgme_data_t dh, const void *buffer, size_t size) { size_t amt = gpgrt_fwrite (buffer, 1, size, dh->data.e_stream); if (amt > 0) return amt; return gpgrt_ferror (dh->data.e_stream) ? -1 : 0; } static gpgme_off_t stream_es_seek (gpgme_data_t dh, gpgme_off_t offset, int whence) { int err; err = gpgrt_fseeko (dh->data.e_stream, offset, whence); if (err) return -1; return gpgrt_ftello (dh->data.e_stream); } static int stream_es_get_fd (gpgme_data_t dh) { gpgrt_fflush (dh->data.e_stream); return gpgrt_fileno (dh->data.e_stream); } static struct _gpgme_data_cbs stream_es_cbs = { stream_es_read, stream_es_write, stream_es_seek, NULL, stream_es_get_fd }; gpgme_error_t gpgme_data_new_from_estream (gpgme_data_t *r_dh, gpgrt_stream_t stream) { gpgme_error_t err; TRACE_BEG (DEBUG_DATA, "gpgme_data_new_from_estream", r_dh, "estream=%p", stream); err = _gpgme_data_new (r_dh, &stream_es_cbs); if (err) return TRACE_ERR (err); (*r_dh)->data.e_stream = stream; - return TRACE_SUC ("dh=%p", *r_dh); + TRACE_SUC ("dh=%p", *r_dh); + return 0; } diff --git a/src/data-fd.c b/src/data-fd.c index 6a915fc0..5c68130f 100644 --- a/src/data-fd.c +++ b/src/data-fd.c @@ -1,86 +1,87 @@ /* data-fd.c - A file descriptor based data object. * Copyright (C) 2002, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME 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. * * GPGME 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-or-later */ #if HAVE_CONFIG_H #include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #include "debug.h" #include "data.h" static gpgme_ssize_t fd_read (gpgme_data_t dh, void *buffer, size_t size) { return read (dh->data.fd, buffer, size); } static gpgme_ssize_t fd_write (gpgme_data_t dh, const void *buffer, size_t size) { return write (dh->data.fd, buffer, size); } static gpgme_off_t fd_seek (gpgme_data_t dh, gpgme_off_t offset, int whence) { return lseek (dh->data.fd, offset, whence); } static int fd_get_fd (gpgme_data_t dh) { return (dh->data.fd); } static struct _gpgme_data_cbs fd_cbs = { fd_read, fd_write, fd_seek, NULL, fd_get_fd }; gpgme_error_t gpgme_data_new_from_fd (gpgme_data_t *r_dh, int fd) { gpgme_error_t err; TRACE_BEG (DEBUG_DATA, "gpgme_data_new_from_fd", r_dh, "fd=0x%x", fd); err = _gpgme_data_new (r_dh, &fd_cbs); if (err) return TRACE_ERR (err); (*r_dh)->data.fd = fd; - return TRACE_SUC ("dh=%p", *r_dh); + TRACE_SUC ("dh=%p", *r_dh); + return 0; } diff --git a/src/data-mem.c b/src/data-mem.c index 915c3e00..f51d2fd7 100644 --- a/src/data-mem.c +++ b/src/data-mem.c @@ -1,305 +1,303 @@ /* data-mem.c - A memory based data object. * Copyright (C) 2002, 2003, 2004, 2007 g10 Code GmbH * * This file is part of GPGME. * * GPGME 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. * * GPGME 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-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include "data.h" #include "util.h" #include "debug.h" static gpgme_ssize_t mem_read (gpgme_data_t dh, void *buffer, size_t size) { size_t amt = dh->data.mem.length - dh->data.mem.offset; const char *src; if (!amt) return 0; if (size < amt) amt = size; src = dh->data.mem.buffer ? dh->data.mem.buffer : dh->data.mem.orig_buffer; memcpy (buffer, src + dh->data.mem.offset, amt); dh->data.mem.offset += amt; return amt; } static gpgme_ssize_t mem_write (gpgme_data_t dh, const void *buffer, size_t size) { size_t unused; if (!dh->data.mem.buffer && dh->data.mem.orig_buffer) { size_t new_size = dh->data.mem.size; char *new_buffer; if (new_size < dh->data.mem.offset + size) new_size = dh->data.mem.offset + size; new_buffer = malloc (new_size); if (!new_buffer) return -1; memcpy (new_buffer, dh->data.mem.orig_buffer, dh->data.mem.length); dh->data.mem.buffer = new_buffer; dh->data.mem.size = new_size; } unused = dh->data.mem.size - dh->data.mem.offset; if (unused < size) { /* Allocate a large enough buffer with exponential backoff. */ #define INITIAL_ALLOC 512 size_t new_size = dh->data.mem.size ? (2 * dh->data.mem.size) : INITIAL_ALLOC; char *new_buffer; if (new_size < dh->data.mem.offset + size) new_size = dh->data.mem.offset + size; new_buffer = realloc (dh->data.mem.buffer, new_size); if (!new_buffer && new_size > dh->data.mem.offset + size) { /* Maybe we were too greedy, try again. */ new_size = dh->data.mem.offset + size; new_buffer = realloc (dh->data.mem.buffer, new_size); } if (!new_buffer) return -1; dh->data.mem.buffer = new_buffer; dh->data.mem.size = new_size; } memcpy (dh->data.mem.buffer + dh->data.mem.offset, buffer, size); dh->data.mem.offset += size; if (dh->data.mem.length < dh->data.mem.offset) dh->data.mem.length = dh->data.mem.offset; return size; } static gpgme_off_t mem_seek (gpgme_data_t dh, gpgme_off_t offset, int whence) { switch (whence) { case SEEK_SET: if (offset < 0 || offset > dh->data.mem.length) { gpg_err_set_errno (EINVAL); return -1; } dh->data.mem.offset = offset; break; case SEEK_CUR: if ((offset > 0 && dh->data.mem.length - dh->data.mem.offset < offset) || (offset < 0 && dh->data.mem.offset < -offset)) { gpg_err_set_errno (EINVAL); return -1; } dh->data.mem.offset += offset; break; case SEEK_END: if (offset > 0 || -offset > dh->data.mem.length) { gpg_err_set_errno (EINVAL); return -1; } dh->data.mem.offset = dh->data.mem.length + offset; break; default: gpg_err_set_errno (EINVAL); return -1; } return dh->data.mem.offset; } static void mem_release (gpgme_data_t dh) { if (dh->data.mem.buffer) free (dh->data.mem.buffer); } static struct _gpgme_data_cbs mem_cbs = { mem_read, mem_write, mem_seek, mem_release, NULL }; /* Create a new data buffer and return it in R_DH. */ gpgme_error_t gpgme_data_new (gpgme_data_t *r_dh) { gpgme_error_t err; TRACE_BEG (DEBUG_DATA, "gpgme_data_new", r_dh, ""); err = _gpgme_data_new (r_dh, &mem_cbs); if (err) return TRACE_ERR (err); - return TRACE_SUC ("dh=%p", *r_dh); + TRACE_SUC ("dh=%p", *r_dh); + return 0; } /* Create a new data buffer filled with SIZE bytes starting from BUFFER. If COPY is zero, copying is delayed until necessary, and the data is taken from the original location when needed. */ gpgme_error_t gpgme_data_new_from_mem (gpgme_data_t *r_dh, const char *buffer, size_t size, int copy) { gpgme_error_t err; TRACE_BEG (DEBUG_DATA, "gpgme_data_new_from_mem", r_dh, "buffer=%p, size=%zu, copy=%i (%s)", buffer, size, copy, copy ? "yes" : "no"); err = _gpgme_data_new (r_dh, &mem_cbs); if (err) return TRACE_ERR (err); if (copy) { char *bufcpy = malloc (size); if (!bufcpy) { int saved_err = gpg_error_from_syserror (); _gpgme_data_release (*r_dh); return TRACE_ERR (saved_err); } memcpy (bufcpy, buffer, size); (*r_dh)->data.mem.buffer = bufcpy; } else (*r_dh)->data.mem.orig_buffer = buffer; (*r_dh)->data.mem.size = size; (*r_dh)->data.mem.length = size; - return TRACE_SUC ("dh=%p", *r_dh); + TRACE_SUC ("dh=%p", *r_dh); + return 0; } /* Destroy the data buffer DH and return a pointer to its content. The memory has be to released with gpgme_free() by the user. It's size is returned in R_LEN. */ char * gpgme_data_release_and_get_mem (gpgme_data_t dh, size_t *r_len) { gpg_error_t err; char *str = NULL; size_t len; int blankout; TRACE_BEG (DEBUG_DATA, "gpgme_data_release_and_get_mem", dh, "r_len=%p", r_len); if (!dh || dh->cbs != &mem_cbs) { gpgme_data_release (dh); TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); return NULL; } err = _gpgme_data_get_prop (dh, 0, DATA_PROP_BLANKOUT, &blankout); if (err) { gpgme_data_release (dh); TRACE_ERR (err); return NULL; } str = dh->data.mem.buffer; len = dh->data.mem.length; if (blankout && len) len = 1; if (!str && dh->data.mem.orig_buffer) { str = malloc (len); if (!str) { int saved_err = gpg_error_from_syserror (); gpgme_data_release (dh); TRACE_ERR (saved_err); return NULL; } if (blankout) memset (str, 0, len); else memcpy (str, dh->data.mem.orig_buffer, len); } else { if (blankout && len) *str = 0; /* Prevent mem_release from releasing the buffer memory. We * must not fail from this point. */ dh->data.mem.buffer = NULL; } if (r_len) *r_len = len; gpgme_data_release (dh); if (r_len) - { - TRACE_SUC ("buffer=%p, len=%zu", str, *r_len); - } + TRACE_SUC ("buffer=%p, len=%zu", str, *r_len); else - { - TRACE_SUC ("buffer=%p", str); - } + TRACE_SUC ("buffer=%p", str); return str; } /* Release the memory returned by gpgme_data_release_and_get_mem() and some other functions. */ void gpgme_free (void *buffer) { TRACE (DEBUG_DATA, "gpgme_free", buffer, ""); if (buffer) free (buffer); } diff --git a/src/data-stream.c b/src/data-stream.c index 19a5a079..1ffb6216 100644 --- a/src/data-stream.c +++ b/src/data-stream.c @@ -1,108 +1,109 @@ /* data-stream.c - A stream based data object. * Copyright (C) 2002, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME 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. * * GPGME 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-or-later */ #if HAVE_CONFIG_H #include #endif #include #ifdef HAVE_SYS_TYPES_H # include #endif #include "debug.h" #include "data.h" static gpgme_ssize_t stream_read (gpgme_data_t dh, void *buffer, size_t size) { size_t amt = fread (buffer, 1, size, dh->data.stream); if (amt > 0) return amt; return ferror (dh->data.stream) ? -1 : 0; } static gpgme_ssize_t stream_write (gpgme_data_t dh, const void *buffer, size_t size) { size_t amt = fwrite (buffer, 1, size, dh->data.stream); if (amt > 0) return amt; return ferror (dh->data.stream) ? -1 : 0; } static gpgme_off_t stream_seek (gpgme_data_t dh, gpgme_off_t offset, int whence) { int err; #ifdef HAVE_FSEEKO err = fseeko (dh->data.stream, offset, whence); #else /* FIXME: Check for overflow, or at least bail at compilation. */ err = fseek (dh->data.stream, offset, whence); #endif if (err) return -1; #ifdef HAVE_FSEEKO return ftello (dh->data.stream); #else return ftell (dh->data.stream); #endif } static int stream_get_fd (gpgme_data_t dh) { fflush (dh->data.stream); return fileno (dh->data.stream); } static struct _gpgme_data_cbs stream_cbs = { stream_read, stream_write, stream_seek, NULL, stream_get_fd }; gpgme_error_t gpgme_data_new_from_stream (gpgme_data_t *r_dh, FILE *stream) { gpgme_error_t err; TRACE_BEG (DEBUG_DATA, "gpgme_data_new_from_stream", r_dh, "stream=%p", stream); err = _gpgme_data_new (r_dh, &stream_cbs); if (err) return TRACE_ERR (err); (*r_dh)->data.stream = stream; - return TRACE_SUC ("dh=%p", *r_dh); + TRACE_SUC ("dh=%p", *r_dh); + return 0; } diff --git a/src/data-user.c b/src/data-user.c index f5bd2efb..0111b2de 100644 --- a/src/data-user.c +++ b/src/data-user.c @@ -1,104 +1,105 @@ /* data-user.c - A user callback based data object. * Copyright (C) 2002, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME 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. * * GPGME 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-or-later */ #if HAVE_CONFIG_H #include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #include #include "debug.h" #include "data.h" static gpgme_ssize_t user_read (gpgme_data_t dh, void *buffer, size_t size) { if (!dh->data.user.cbs->read) { gpg_err_set_errno (EBADF); return -1; } return (*dh->data.user.cbs->read) (dh->data.user.handle, buffer, size); } static gpgme_ssize_t user_write (gpgme_data_t dh, const void *buffer, size_t size) { if (!dh->data.user.cbs->write) { gpg_err_set_errno (EBADF); return -1; } return (*dh->data.user.cbs->write) (dh->data.user.handle, buffer, size); } static gpgme_off_t user_seek (gpgme_data_t dh, gpgme_off_t offset, int whence) { if (!dh->data.user.cbs->seek) { gpg_err_set_errno (EBADF); return -1; } return (*dh->data.user.cbs->seek) (dh->data.user.handle, offset, whence); } static void user_release (gpgme_data_t dh) { if (dh->data.user.cbs->release) (*dh->data.user.cbs->release) (dh->data.user.handle); } static struct _gpgme_data_cbs user_cbs = { user_read, user_write, user_seek, user_release, NULL }; gpgme_error_t gpgme_data_new_from_cbs (gpgme_data_t *r_dh, gpgme_data_cbs_t cbs, void *handle) { gpgme_error_t err; TRACE_BEG (DEBUG_DATA, "gpgme_data_new_from_cbs", r_dh, "handle=%p", handle); err = _gpgme_data_new (r_dh, &user_cbs); if (err) return TRACE_ERR (err); (*r_dh)->data.user.cbs = cbs; (*r_dh)->data.user.handle = handle; - return TRACE_SUC ("dh=%p", *r_dh); + TRACE_SUC ("dh=%p", *r_dh); + return 0; } diff --git a/src/debug.h b/src/debug.h index 06e6d02c..7ef8cf23 100644 --- a/src/debug.h +++ b/src/debug.h @@ -1,198 +1,228 @@ /* debug.h - interface to debugging functions Copyright (C) 2002, 2004, 2005, 2007 g10 Code GmbH This file is part of GPGME. GPGME 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. GPGME 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef DEBUG_H #define DEBUG_H #include #ifdef HAVE_STDINT_H #include #endif #include "gpgme.h" /* Required for gpgme_error stuff. */ /* Indirect stringification, requires __STDC__ to work. */ #define STRINGIFY(v) #v #define XSTRINGIFY(v) STRINGIFY(v) /* * The debug levels. * * Note that TRACE_LOGBUFX uses the current debug level + 1. */ #define DEBUG_INIT 1 #define DEBUG_GLOBAL 2 #define DEBUG_CTX 3 #define DEBUG_ENGINE 4 #define DEBUG_DATA 5 #define DEBUG_ASSUAN 6 #define DEBUG_SYSIO 7 /* Remove path components from filenames (i.e. __FILE__) for cleaner logs. */ static inline const char *_gpgme_debug_srcname (const char *file) GPGME_GCC_A_PURE; static inline const char * _gpgme_debug_srcname (const char *file) { const char *s = strrchr (file, '/'); return s? s+1:file; } /* Initialization helper function; see debug.c. */ int _gpgme_debug_set_debug_envvar (const char *value); /* Called early to initialize the logging. */ void _gpgme_debug_subsystem_init (void); /* Log the formatted string FORMAT at debug level LEVEL or higher. */ int _gpgme_debug (int level, int mode, const char *func, const char *tagname, const char *tagvalue, const char *format, ...) GPGRT_ATTR_PRINTF(6,7); /* Start a new debug line in *LINE, logged at level LEVEL or higher, and starting with the formatted string FORMAT. */ void _gpgme_debug_begin (void **helper, int level, const char *format, ...); /* Add the formatted string FORMAT to the debug line *LINE. */ void _gpgme_debug_add (void **helper, const char *format, ...); /* Finish construction of *LINE and send it to the debug output stream. */ void _gpgme_debug_end (void **helper); void _gpgme_debug_buffer (int lvl, const char *const fmt, const char *const func, const char *const buffer, size_t len); void _gpgme_debug_frame_begin (void); int _gpgme_debug_frame_end (void); static inline gpgme_error_t _gpgme_trace_gpgme_error (gpgme_error_t err, const char *file, int line) { _gpgme_debug (DEBUG_ENGINE, -1, NULL, NULL, NULL, "%s:%d: returning error: %s\n", _gpgme_debug_srcname (file), line, gpgme_strerror (err)); return err; } /* Trace support. */ /* FIXME: For now. */ #define _gpgme_debug_trace() 1 #define _TRACE(lvl, name, tag) \ int _gpgme_trace_level = lvl; \ const char *const _gpgme_trace_func = name; \ const char *const _gpgme_trace_tagname = STRINGIFY (tag); \ void *_gpgme_trace_tag = (void *) (uintptr_t) tag; \ _gpgme_debug_frame_begin () -#define TRACE_BEG(lvl, name, tag, ...) \ +/* Note: We can't protect this with a do-while block. */ +#define TRACE_BEG(lvl, name, tag, ...) \ _TRACE (lvl, name, tag); \ - _gpgme_debug (_gpgme_trace_level, 1, \ + _gpgme_debug (_gpgme_trace_level, 1, \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ __VA_ARGS__) -#define TRACE(lvl, name, tag, ...) \ - _gpgme_debug_frame_begin (), \ - _gpgme_debug (lvl, 0, \ - name, STRINGIFY (tag), (void *) (uintptr_t) tag, \ - __VA_ARGS__), \ - _gpgme_debug_frame_end () - -#define TRACE_ERR(err) \ - err == 0 ? (TRACE_SUC ("")) : \ - (_gpgme_debug (_gpgme_trace_level, -1, NULL, NULL, NULL, \ - "%s:%d: error: %s <%s>\n", \ - _gpgme_trace_func, __LINE__, gpgme_strerror (err), \ - gpgme_strsource (err)), _gpgme_debug_frame_end (), (err)) - - -/* The cast to void suppresses GCC warnings. */ -#define TRACE_SYSRES(res) \ - res >= 0 ? ((void) (TRACE_SUC ("result=%i", res)), (res)) : \ - (_gpgme_debug (_gpgme_trace_level, -1, NULL, NULL, NULL, \ - "%s: error: %s\n", \ - _gpgme_trace_func, strerror (errno)), \ - _gpgme_debug_frame_end (), (res)) -#define TRACE_SYSERR(res) \ - res == 0 ? ((void) (TRACE_SUC ("result=%i", res)), (res)) : \ - (_gpgme_debug (_gpgme_trace_level, -1, NULL, NULL, NULL, \ - "%s: error: %s\n", \ - _gpgme_trace_func, strerror (res)), \ - _gpgme_debug_frame_end (), (res)) -#define TRACE_SYSERR_NR(res) \ - do { res == 0 ? ((void) (TRACE_SUC ("result=%i", res)), (res)) : \ - (_gpgme_debug (_gpgme_trace_level, -1, NULL, NULL, NULL, \ - "%s: error: %s\n", \ - _gpgme_trace_func, strerror (res)), \ - _gpgme_debug_frame_end ()); } while (0) - -#define TRACE_SUC(...) \ - _gpgme_debug (_gpgme_trace_level, 3, _gpgme_trace_func, NULL, NULL, \ - __VA_ARGS__), _gpgme_debug_frame_end () - -#define TRACE_LOG(...) \ - _gpgme_debug (_gpgme_trace_level, 2, \ - _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ - __VA_ARGS__) - -#define TRACE_LOGBUF(buf, len) \ - _gpgme_debug_buffer (_gpgme_trace_level, "%s: check: %s", \ - _gpgme_trace_func, buf, len) - -#define TRACE_LOGBUFX(buf, len) \ - _gpgme_debug_buffer (_gpgme_trace_level+1, "%s: check: %s", \ - _gpgme_trace_func, buf, len) - -#define TRACE_SEQ(hlp,fmt) \ - _gpgme_debug_begin (&(hlp), _gpgme_trace_level, \ - "%s: check: %s=%p, " fmt, _gpgme_trace_func, \ - _gpgme_trace_tagname, _gpgme_trace_tag) +#define TRACE(lvl, name, tag, ...) do { \ + _gpgme_debug_frame_begin (); \ + _gpgme_debug (lvl, 0, name, STRINGIFY (tag), (void *)(uintptr_t)tag, \ + __VA_ARGS__); \ + _gpgme_debug_frame_end (); \ + } while (0) + + +/* Trace a gpg-error and return it. */ +#define TRACE_ERR(err) \ + _trace_err ((err), _gpgme_trace_level, _gpgme_trace_func, __LINE__) +static inline gpg_error_t +_trace_err (gpg_error_t err, int lvl, const char *func, int line) +{ + if (!err) + _gpgme_debug (lvl, 3, func, NULL, NULL, ""); + else + _gpgme_debug (lvl, -1, NULL, NULL, NULL, + "%s:%d: error: %s <%s>\n", + func, line, gpgme_strerror (err), gpgme_strsource (err)); + _gpgme_debug_frame_end (); + return err; +} + +/* Trace a system call result and return it. */ +#define TRACE_SYSRES(res) \ + _trace_sysres ((res), _gpgme_trace_level, _gpgme_trace_func, __LINE__) +static inline int +_trace_sysres (int res, int lvl, const char *func, int line) +{ + if (res >= 0) + _gpgme_debug (lvl, 3, func, NULL, NULL, "result=%d", res); + else + _gpgme_debug (lvl, -1, NULL, NULL, NULL, + "%s:%d: error: %s (%d)\n", + func, line, strerror (res), res); + _gpgme_debug_frame_end (); + return res; +} + +/* Trace a system call error and return it. */ +#define TRACE_SYSERR(rc) \ + _trace_syserr ((rc), _gpgme_trace_level, _gpgme_trace_func, __LINE__) +static inline int +_trace_syserr (int rc, int lvl, const char *func, int line) +{ + if (!rc) + _gpgme_debug (lvl, 3, func, NULL, NULL, "result=0"); + else + _gpgme_debug (lvl, -1, NULL, NULL, NULL, + "%s:%d: error: %s (%d)\n", + func, line, strerror (rc), rc); + _gpgme_debug_frame_end (); + return rc; +} + +#define TRACE_SUC(...) do { \ + _gpgme_debug (_gpgme_trace_level, 3, _gpgme_trace_func, NULL, NULL, \ + __VA_ARGS__); \ + _gpgme_debug_frame_end (); \ + } while (0) + +#define TRACE_LOG(...) do { \ + _gpgme_debug (_gpgme_trace_level, 2, \ + _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ + __VA_ARGS__); \ + } while (0) + +#define TRACE_LOGBUF(buf, len) do { \ + _gpgme_debug_buffer (_gpgme_trace_level, "%s: check: %s", \ + _gpgme_trace_func, buf, len); \ + } while (0) + +#define TRACE_LOGBUFX(buf, len) do { \ + _gpgme_debug_buffer (_gpgme_trace_level+1, "%s: check: %s", \ + _gpgme_trace_func, buf, len); \ + } while (0) + +#define TRACE_SEQ(hlp,fmt) do { \ + _gpgme_debug_begin (&(hlp), _gpgme_trace_level, \ + "%s: check: %s=%p, " fmt, _gpgme_trace_func, \ + _gpgme_trace_tagname, _gpgme_trace_tag); \ + } while (0) + #define TRACE_ADD0(hlp,fmt) \ _gpgme_debug_add (&(hlp), fmt) #define TRACE_ADD1(hlp,fmt,a) \ _gpgme_debug_add (&(hlp), fmt, (a)) #define TRACE_ADD2(hlp,fmt,a,b) \ _gpgme_debug_add (&(hlp), fmt, (a), (b)) #define TRACE_ADD3(hlp,fmt,a,b,c) \ _gpgme_debug_add (&(hlp), fmt, (a), (b), (c)) #define TRACE_END(hlp,fmt) \ _gpgme_debug_add (&(hlp), fmt); \ _gpgme_debug_end (&(hlp)) + #define TRACE_ENABLED(hlp) (!!(hlp)) /* And finally a simple macro to trace the location of an error code. This macro is independent of the other trace macros and may be used without any preconditions. */ #define trace_gpg_error(e) \ _gpgme_trace_gpgme_error (gpg_error (e), __FILE__, __LINE__) #endif /* DEBUG_H */ diff --git a/src/gpgme.c b/src/gpgme.c index c4a1da11..a0a6c6bc 100644 --- a/src/gpgme.c +++ b/src/gpgme.c @@ -1,1294 +1,1295 @@ /* gpgme.c - GnuPG Made Easy. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2012, * 2014, 2015 g10 Code GmbH * * This file is part of GPGME. * * GPGME 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. * * GPGME 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-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #ifdef HAVE_LOCALE_H #include #endif #include "util.h" #include "context.h" #include "ops.h" #include "wait.h" #include "debug.h" #include "priv-io.h" #include "sys-util.h" #include "mbox-util.h" /* The default locale. */ DEFINE_STATIC_LOCK (def_lc_lock); static char *def_lc_ctype; static char *def_lc_messages; gpgme_error_t _gpgme_selftest = GPG_ERR_NOT_OPERATIONAL; /* Protects all reference counters in result structures. All other accesses to a result structure are read only. */ DEFINE_STATIC_LOCK (result_ref_lock); /* Set the global flag NAME to VALUE. Return 0 on success. Note that this function does not use gpgme_error and thus a non-zero return value merely means "error". Certain flags may be set before gpgme_check_version is called. See the manual for a description of supported flags. The caller must assure that this function is called only by one thread at a time. */ int gpgme_set_global_flag (const char *name, const char *value) { if (!name || !value) return -1; else if (!strcmp (name, "debug")) return _gpgme_debug_set_debug_envvar (value); else if (!strcmp (name, "disable-gpgconf")) { _gpgme_dirinfo_disable_gpgconf (); return 0; } else if (!strcmp (name, "require-gnupg")) return _gpgme_set_engine_minimal_version (value); else if (!strcmp (name, "gpgconf-name")) return _gpgme_set_default_gpgconf_name (value); else if (!strcmp (name, "gpg-name")) return _gpgme_set_default_gpg_name (value); else if (!strcmp (name, "w32-inst-dir")) return _gpgme_set_override_inst_dir (value); else return -1; } /* Create a new context as an environment for GPGME crypto operations. */ gpgme_error_t gpgme_new (gpgme_ctx_t *r_ctx) { gpgme_error_t err; gpgme_ctx_t ctx; TRACE_BEG (DEBUG_CTX, "gpgme_new", r_ctx, ""); if (_gpgme_selftest) return TRACE_ERR (_gpgme_selftest); if (!r_ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); ctx = calloc (1, sizeof *ctx); if (!ctx) return TRACE_ERR (gpg_error_from_syserror ()); INIT_LOCK (ctx->lock); err = _gpgme_engine_info_copy (&ctx->engine_info); if (!err && !ctx->engine_info) err = gpg_error (GPG_ERR_NO_ENGINE); if (err) { free (ctx); return TRACE_ERR (err); } ctx->keylist_mode = GPGME_KEYLIST_MODE_LOCAL; ctx->include_certs = GPGME_INCLUDE_CERTS_DEFAULT; ctx->protocol = GPGME_PROTOCOL_OpenPGP; ctx->sub_protocol = GPGME_PROTOCOL_DEFAULT; _gpgme_fd_table_init (&ctx->fdt); LOCK (def_lc_lock); if (def_lc_ctype) { ctx->lc_ctype = strdup (def_lc_ctype); if (!ctx->lc_ctype) { int saved_err = gpg_error_from_syserror (); UNLOCK (def_lc_lock); _gpgme_engine_info_release (ctx->engine_info); free (ctx); return TRACE_ERR (saved_err); } } else def_lc_ctype = NULL; if (def_lc_messages) { ctx->lc_messages = strdup (def_lc_messages); if (!ctx->lc_messages) { int saved_err = gpg_error_from_syserror (); UNLOCK (def_lc_lock); if (ctx->lc_ctype) free (ctx->lc_ctype); _gpgme_engine_info_release (ctx->engine_info); free (ctx); return TRACE_ERR (saved_err); } } else def_lc_messages = NULL; UNLOCK (def_lc_lock); *r_ctx = ctx; - return TRACE_SUC ("ctx=%p", ctx); + TRACE_SUC ("ctx=%p", ctx); + return 0; } gpgme_error_t _gpgme_cancel_with_err (gpgme_ctx_t ctx, gpg_error_t ctx_err, gpg_error_t op_err) { gpgme_error_t err; struct gpgme_io_event_done_data data; TRACE_BEG (DEBUG_CTX, "_gpgme_cancel_with_err", ctx, "ctx_err=%i, op_err=%i", ctx_err, op_err); if (ctx_err) { err = _gpgme_engine_cancel (ctx->engine); if (err) return TRACE_ERR (err); } else { err = _gpgme_engine_cancel_op (ctx->engine); if (err) return TRACE_ERR (err); } data.err = ctx_err; data.op_err = op_err; _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &data); return TRACE_ERR (0); } /* Cancel a pending asynchronous operation. */ gpgme_error_t gpgme_cancel (gpgme_ctx_t ctx) { gpg_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_cancel", ctx, ""); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_cancel_with_err (ctx, gpg_error (GPG_ERR_CANCELED), 0); return TRACE_ERR (err); } /* Cancel a pending operation asynchronously. */ gpgme_error_t gpgme_cancel_async (gpgme_ctx_t ctx) { TRACE_BEG (DEBUG_CTX, "gpgme_cancel_async", ctx, ""); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); LOCK (ctx->lock); ctx->canceled = 1; UNLOCK (ctx->lock); return TRACE_ERR (0); } /* Release all resources associated with the given context. */ void gpgme_release (gpgme_ctx_t ctx) { TRACE (DEBUG_CTX, "gpgme_release", ctx, ""); if (!ctx) return; _gpgme_engine_release (ctx->engine); ctx->engine = NULL; _gpgme_fd_table_deinit (&ctx->fdt); _gpgme_release_result (ctx); _gpgme_signers_clear (ctx); _gpgme_sig_notation_clear (ctx); free (ctx->sender); free (ctx->signers); free (ctx->lc_ctype); free (ctx->lc_messages); free (ctx->override_session_key); free (ctx->request_origin); free (ctx->auto_key_locate); free (ctx->trust_model); _gpgme_engine_info_release (ctx->engine_info); ctx->engine_info = NULL; DESTROY_LOCK (ctx->lock); free (ctx); } void gpgme_result_ref (void *result) { struct ctx_op_data *data; if (! result) return; data = (void*)((char*)result - sizeof (struct ctx_op_data)); assert (data->magic == CTX_OP_DATA_MAGIC); LOCK (result_ref_lock); data->references++; UNLOCK (result_ref_lock); } void gpgme_result_unref (void *result) { struct ctx_op_data *data; if (! result) return; data = (void*)((char*)result - sizeof (struct ctx_op_data)); assert (data->magic == CTX_OP_DATA_MAGIC); LOCK (result_ref_lock); if (--data->references) { UNLOCK (result_ref_lock); return; } UNLOCK (result_ref_lock); if (data->cleanup) (*data->cleanup) (data->hook); free (data); } void _gpgme_release_result (gpgme_ctx_t ctx) { struct ctx_op_data *data = ctx->op_data; while (data) { struct ctx_op_data *next_data = data->next; data->next = NULL; gpgme_result_unref (data->hook); data = next_data; } ctx->op_data = NULL; } gpgme_error_t gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol) { TRACE_BEG (DEBUG_CTX, "gpgme_set_protocol", ctx, "protocol=%i (%s)", protocol, gpgme_get_protocol_name (protocol) ? gpgme_get_protocol_name (protocol) : "invalid"); if (protocol != GPGME_PROTOCOL_OpenPGP && protocol != GPGME_PROTOCOL_CMS && protocol != GPGME_PROTOCOL_GPGCONF && protocol != GPGME_PROTOCOL_ASSUAN && protocol != GPGME_PROTOCOL_G13 && protocol != GPGME_PROTOCOL_UISERVER && protocol != GPGME_PROTOCOL_SPAWN) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (ctx->protocol != protocol) { /* Shut down the engine when switching protocols. */ if (ctx->engine) { TRACE_LOG ("releasing ctx->engine=%p", ctx->engine); _gpgme_engine_release (ctx->engine); ctx->engine = NULL; } ctx->protocol = protocol; } return TRACE_ERR (0); } gpgme_protocol_t gpgme_get_protocol (gpgme_ctx_t ctx) { TRACE (DEBUG_CTX, "gpgme_get_protocol", ctx, "ctx->protocol=%i (%s)", ctx->protocol, gpgme_get_protocol_name (ctx->protocol) ? gpgme_get_protocol_name (ctx->protocol) : "invalid"); return ctx->protocol; } gpgme_error_t gpgme_set_sub_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol) { TRACE (DEBUG_CTX, "gpgme_set_sub_protocol", ctx, "protocol=%i (%s)", protocol, gpgme_get_protocol_name (protocol) ? gpgme_get_protocol_name (protocol) : "invalid"); if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); ctx->sub_protocol = protocol; return 0; } gpgme_protocol_t gpgme_get_sub_protocol (gpgme_ctx_t ctx) { TRACE (DEBUG_CTX, "gpgme_get_sub_protocol", ctx, "ctx->sub_protocol=%i (%s)", ctx->sub_protocol, gpgme_get_protocol_name (ctx->sub_protocol) ? gpgme_get_protocol_name (ctx->sub_protocol) : "invalid"); return ctx->sub_protocol; } const char * gpgme_get_protocol_name (gpgme_protocol_t protocol) { switch (protocol) { case GPGME_PROTOCOL_OpenPGP: return "OpenPGP"; case GPGME_PROTOCOL_CMS: return "CMS"; case GPGME_PROTOCOL_GPGCONF: return "GPGCONF"; case GPGME_PROTOCOL_ASSUAN: return "Assuan"; case GPGME_PROTOCOL_G13: return "G13"; case GPGME_PROTOCOL_UISERVER: return "UIServer"; case GPGME_PROTOCOL_SPAWN: return "Spawn"; case GPGME_PROTOCOL_DEFAULT: return "default"; case GPGME_PROTOCOL_UNKNOWN: return "unknown"; default: return NULL; } } /* Store the sender's address in the context. ADDRESS is addr-spec of * mailbox but my also be a complete mailbox, in which case this * function extracts the addr-spec from it. Returns 0 on success or * an error code if no valid addr-spec could be extracted from * ADDRESS. */ gpgme_error_t gpgme_set_sender (gpgme_ctx_t ctx, const char *address) { char *p = NULL; TRACE_BEG (DEBUG_CTX, "gpgme_set_sender", ctx, "sender='%s'", address?address:"(null)"); if (!ctx || (address && !(p = _gpgme_mailbox_from_userid (address)))) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); free (ctx->sender); ctx->sender = p; return TRACE_ERR (0); } /* Return the sender's address (addr-spec part) from the context or * NULL if none was set. The returned value is valid as long as the * CTX is valid and gpgme_set_sender has not been used. */ const char * gpgme_get_sender (gpgme_ctx_t ctx) { TRACE (DEBUG_CTX, "gpgme_get_sender", ctx, "sender='%s'", ctx?ctx->sender:""); return ctx->sender; } /* Enable or disable the use of an ascii armor for all output. */ void gpgme_set_armor (gpgme_ctx_t ctx, int use_armor) { TRACE (DEBUG_CTX, "gpgme_set_armor", ctx, "use_armor=%i (%s)", use_armor, use_armor ? "yes" : "no"); if (!ctx) return; ctx->use_armor = !!use_armor; } /* Return the state of the armor flag. */ int gpgme_get_armor (gpgme_ctx_t ctx) { TRACE (DEBUG_CTX, "gpgme_get_armor", ctx, "ctx->use_armor=%i (%s)", ctx->use_armor, ctx->use_armor ? "yes" : "no"); return ctx->use_armor; } /* Set the flag NAME for CTX to VALUE. Please consult the manual for * a description of the flags. */ gpgme_error_t gpgme_set_ctx_flag (gpgme_ctx_t ctx, const char *name, const char *value) { gpgme_error_t err = 0; int abool; TRACE (DEBUG_CTX, "gpgme_set_ctx_flag", ctx, "name='%s' value='%s'", name? name:"(null)", value?value:"(null)"); abool = (value && *value)? !!atoi (value) : 0; if (!ctx || !name || !value) err = gpg_error (GPG_ERR_INV_VALUE); else if (!strcmp (name, "redraw")) { ctx->redraw_suggested = abool; } else if (!strcmp (name, "full-status")) { ctx->full_status = abool; } else if (!strcmp (name, "raw-description")) { ctx->raw_description = abool; } else if (!strcmp (name, "export-session-key")) { ctx->export_session_keys = abool; } else if (!strcmp (name, "override-session-key")) { free (ctx->override_session_key); ctx->override_session_key = strdup (value); if (!ctx->override_session_key) err = gpg_error_from_syserror (); } else if (!strcmp (name, "auto-key-retrieve")) { ctx->auto_key_retrieve = abool; } else if (!strcmp (name, "request-origin")) { free (ctx->request_origin); ctx->request_origin = strdup (value); if (!ctx->request_origin) err = gpg_error_from_syserror (); } else if (!strcmp (name, "no-symkey-cache")) { ctx->no_symkey_cache = abool; } else if (!strcmp (name, "ignore-mdc-error")) { ctx->ignore_mdc_error = abool; } else if (!strcmp (name, "auto-key-locate")) { free (ctx->auto_key_locate); ctx->auto_key_locate = strdup (value); if (!ctx->auto_key_locate) err = gpg_error_from_syserror (); } else if (!strcmp (name, "trust-model")) { free (ctx->trust_model); ctx->trust_model = strdup (value); if (!ctx->trust_model) err = gpg_error_from_syserror (); } else err = gpg_error (GPG_ERR_UNKNOWN_NAME); return err; } /* Get the context flag named NAME. See gpgme_set_ctx_flag for a list * of valid names. If the NAME is unknown NULL is returned. For a * boolean flag an empty string is returned for False and the string * "1" for True; thus either atoi or a simple string test can be * used. */ const char * gpgme_get_ctx_flag (gpgme_ctx_t ctx, const char *name) { if (!ctx || !name) return NULL; else if (!strcmp (name, "redraw")) { return ctx->redraw_suggested? "1":""; } else if (!strcmp (name, "full-status")) { return ctx->full_status? "1":""; } else if (!strcmp (name, "raw-description")) { return ctx->raw_description? "1":""; } else if (!strcmp (name, "export-session-key")) { return ctx->export_session_keys? "1":""; } else if (!strcmp (name, "override-session-key")) { return ctx->override_session_key? ctx->override_session_key : ""; } else if (!strcmp (name, "auto-key-retrieve")) { return ctx->auto_key_retrieve? "1":""; } else if (!strcmp (name, "request-origin")) { return ctx->request_origin? ctx->request_origin : ""; } else if (!strcmp (name, "no-symkey-cache")) { return ctx->no_symkey_cache? "1":""; } else if (!strcmp (name, "ignore-mdc-error")) { return ctx->ignore_mdc_error? "1":""; } else if (!strcmp (name, "auto-key-locate")) { return ctx->auto_key_locate? ctx->auto_key_locate : ""; } else return NULL; } /* Enable or disable the use of the special textmode. Textmode is for example used for the RFC2015 signatures; note that the updated RFC 3156 mandates that the MUA does some preparations so that textmode is not needed anymore. */ void gpgme_set_textmode (gpgme_ctx_t ctx, int use_textmode) { TRACE (DEBUG_CTX, "gpgme_set_textmode", ctx, "use_textmode=%i (%s)", use_textmode, use_textmode ? "yes" : "no"); if (!ctx) return; ctx->use_textmode = !!use_textmode; } /* Return the state of the textmode flag. */ int gpgme_get_textmode (gpgme_ctx_t ctx) { TRACE (DEBUG_CTX, "gpgme_get_textmode", ctx, "ctx->use_textmode=%i (%s)", ctx->use_textmode, ctx->use_textmode ? "yes" : "no"); return ctx->use_textmode; } /* Enable offline mode for this context. In offline mode dirmngr will be disabled. */ void gpgme_set_offline (gpgme_ctx_t ctx, int offline) { TRACE (DEBUG_CTX, "gpgme_set_offline", ctx, "offline=%i (%s)", offline, offline ? "yes" : "no"); if (!ctx) return; ctx->offline = !!offline; } /* Return the state of the offline flag. */ int gpgme_get_offline (gpgme_ctx_t ctx) { TRACE (DEBUG_CTX, "gpgme_get_offline", ctx, "ctx->offline=%i (%s)", ctx->offline, ctx->offline ? "yes" : "no"); return ctx->offline; } /* Set the number of certifications to include in an S/MIME message. The default is GPGME_INCLUDE_CERTS_DEFAULT. -1 means all certs, and -2 means all certs except the root cert. */ void gpgme_set_include_certs (gpgme_ctx_t ctx, int nr_of_certs) { if (!ctx) return; if (nr_of_certs == GPGME_INCLUDE_CERTS_DEFAULT) ctx->include_certs = GPGME_INCLUDE_CERTS_DEFAULT; else if (nr_of_certs < -2) ctx->include_certs = -2; else ctx->include_certs = nr_of_certs; TRACE (DEBUG_CTX, "gpgme_set_include_certs", ctx, "nr_of_certs=%i%s", nr_of_certs, nr_of_certs == ctx->include_certs ? "" : " (-2)"); } /* Get the number of certifications to include in an S/MIME message. */ int gpgme_get_include_certs (gpgme_ctx_t ctx) { TRACE (DEBUG_CTX, "gpgme_get_include_certs", ctx, "ctx->include_certs=%i", ctx->include_certs); return ctx->include_certs; } /* This function changes the default behaviour of the keylisting functions. MODE is a bitwise-OR of the GPGME_KEYLIST_* flags. The default mode is GPGME_KEYLIST_MODE_LOCAL. */ gpgme_error_t gpgme_set_keylist_mode (gpgme_ctx_t ctx, gpgme_keylist_mode_t mode) { TRACE (DEBUG_CTX, "gpgme_set_keylist_mode", ctx, "keylist_mode=0x%x", mode); if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); ctx->keylist_mode = mode; return 0; } /* This function returns the default behaviour of the keylisting functions. */ gpgme_keylist_mode_t gpgme_get_keylist_mode (gpgme_ctx_t ctx) { TRACE (DEBUG_CTX, "gpgme_get_keylist_mode", ctx, "ctx->keylist_mode=0x%x", ctx->keylist_mode); return ctx->keylist_mode; } /* Set the pinentry mode for CTX to MODE. */ gpgme_error_t gpgme_set_pinentry_mode (gpgme_ctx_t ctx, gpgme_pinentry_mode_t mode) { TRACE (DEBUG_CTX, "gpgme_set_pinentry_mode", ctx, "pinentry_mode=%u", (unsigned int)mode); if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); switch (mode) { case GPGME_PINENTRY_MODE_DEFAULT: case GPGME_PINENTRY_MODE_ASK: case GPGME_PINENTRY_MODE_CANCEL: case GPGME_PINENTRY_MODE_ERROR: case GPGME_PINENTRY_MODE_LOOPBACK: break; default: return gpg_error (GPG_ERR_INV_VALUE); } ctx->pinentry_mode = mode; return 0; } /* Get the pinentry mode of CTX. */ gpgme_pinentry_mode_t gpgme_get_pinentry_mode (gpgme_ctx_t ctx) { TRACE (DEBUG_CTX, "gpgme_get_pinentry_mode", ctx, "ctx->pinentry_mode=%u", (unsigned int)ctx->pinentry_mode); return ctx->pinentry_mode; } /* This function sets a callback function to be used to pass a passphrase to gpg. */ void gpgme_set_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, void *cb_value) { TRACE (DEBUG_CTX, "gpgme_set_passphrase_cb", ctx, "passphrase_cb=%p/%p", cb, cb_value); if (!ctx) return; ctx->passphrase_cb = cb; ctx->passphrase_cb_value = cb_value; } /* This function returns the callback function to be used to pass a passphrase to the crypto engine. */ void gpgme_get_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t *r_cb, void **r_cb_value) { TRACE (DEBUG_CTX, "gpgme_get_passphrase_cb", ctx, "ctx->passphrase_cb=%p/%p", ctx->passphrase_cb, ctx->passphrase_cb_value); if (r_cb) *r_cb = ctx->passphrase_cb; if (r_cb_value) *r_cb_value = ctx->passphrase_cb_value; } /* This function sets a callback function to be used as a progress indicator. */ void gpgme_set_progress_cb (gpgme_ctx_t ctx, gpgme_progress_cb_t cb, void *cb_value) { TRACE (DEBUG_CTX, "gpgme_set_progress_cb", ctx, "progress_cb=%p/%p", cb, cb_value); if (!ctx) return; ctx->progress_cb = cb; ctx->progress_cb_value = cb_value; } /* This function returns the callback function to be used as a progress indicator. */ void gpgme_get_progress_cb (gpgme_ctx_t ctx, gpgme_progress_cb_t *r_cb, void **r_cb_value) { TRACE (DEBUG_CTX, "gpgme_get_progress_cb", ctx, "ctx->progress_cb=%p/%p", ctx->progress_cb, ctx->progress_cb_value); if (r_cb) *r_cb = ctx->progress_cb; if (r_cb_value) *r_cb_value = ctx->progress_cb_value; } /* This function sets a callback function to be used as a status message forwarder. */ void gpgme_set_status_cb (gpgme_ctx_t ctx, gpgme_status_cb_t cb, void *cb_value) { TRACE (DEBUG_CTX, "gpgme_set_status_cb", ctx, "status_cb=%p/%p", cb, cb_value); if (!ctx) return; ctx->status_cb = cb; ctx->status_cb_value = cb_value; } /* This function returns the callback function to be used as a status message forwarder. */ void gpgme_get_status_cb (gpgme_ctx_t ctx, gpgme_status_cb_t *r_cb, void **r_cb_value) { TRACE (DEBUG_CTX, "gpgme_get_status_cb", ctx, "ctx->status_cb=%p/%p", ctx ? ctx->status_cb : NULL, ctx ? ctx->status_cb_value : NULL); if (r_cb) *r_cb = NULL; if (r_cb_value) *r_cb_value = NULL; if (!ctx || !ctx->status_cb) return; if (r_cb) *r_cb = ctx->status_cb; if (r_cb_value) *r_cb_value = ctx->status_cb_value; } /* Set the I/O callback functions for CTX to IO_CBS. */ void gpgme_set_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs) { if (!ctx) return; if (io_cbs) { TRACE (DEBUG_CTX, "gpgme_set_io_cbs", ctx, "io_cbs=%p (add=%p/%p, remove=%p, event=%p/%p", io_cbs, io_cbs->add, io_cbs->add_priv, io_cbs->remove, io_cbs->event, io_cbs->event_priv); ctx->io_cbs = *io_cbs; } else { TRACE (DEBUG_CTX, "gpgme_set_io_cbs", ctx, "io_cbs=%p (default)", io_cbs); ctx->io_cbs.add = NULL; ctx->io_cbs.add_priv = NULL; ctx->io_cbs.remove = NULL; ctx->io_cbs.event = NULL; ctx->io_cbs.event_priv = NULL; } } /* This function provides access to the internal read function; it is normally not used. */ gpgme_ssize_t gpgme_io_read (int fd, void *buffer, size_t count) { int ret; TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_read", fd, "buffer=%p, count=%zu", buffer, count); ret = _gpgme_io_read (fd, buffer, count); return TRACE_SYSRES (ret); } /* This function provides access to the internal write function. It is to be used by user callbacks to return data to gpgme. See gpgme_passphrase_cb_t and gpgme_edit_cb_t. */ gpgme_ssize_t gpgme_io_write (int fd, const void *buffer, size_t count) { int ret; TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_write", fd, "buffer=%p, count=%zu", buffer, count); ret = _gpgme_io_write (fd, buffer, count); return TRACE_SYSRES (ret); } /* This function provides access to the internal write function. It is to be used by user callbacks to return data to gpgme. See gpgme_passphrase_cb_t and gpgme_edit_cb_t. Note that this is a variant of gpgme_io_write which guarantees that all COUNT bytes are written or an error is return. Returns: 0 on success or -1 on error and the sets errno. */ int gpgme_io_writen (int fd, const void *buffer_arg, size_t count) { const char *buffer = buffer_arg; int ret = 0; TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_writen", fd, "buffer=%p, count=%zu", buffer, count); while (count) { ret = _gpgme_io_write (fd, buffer, count); if (ret < 0) break; buffer += ret; count -= ret; ret = 0; } return TRACE_SYSRES (ret); } /* This function returns the callback function for I/O. */ void gpgme_get_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs) { TRACE (DEBUG_CTX, "gpgme_get_io_cbs", ctx, "io_cbs=%p, ctx->io_cbs.add=%p/%p, .remove=%p, .event=%p/%p", io_cbs, io_cbs->add, io_cbs->add_priv, io_cbs->remove, io_cbs->event, io_cbs->event_priv); *io_cbs = ctx->io_cbs; } /* This function sets the locale for the context CTX, or the default locale if CTX is a null pointer. */ gpgme_error_t gpgme_set_locale (gpgme_ctx_t ctx, int category, const char *value) { int failed = 0; char *new_lc_ctype = NULL; char *new_lc_messages = NULL; TRACE_BEG (DEBUG_CTX, "gpgme_set_locale", ctx, "category=%i, value=%s", category, value ? value : "(null)"); #define PREPARE_ONE_LOCALE(lcat, ucat) \ if (!failed && value \ && (category == LC_ALL || category == LC_ ## ucat)) \ { \ new_lc_ ## lcat = strdup (value); \ if (!new_lc_ ## lcat) \ failed = 1; \ } #ifdef LC_CTYPE PREPARE_ONE_LOCALE (ctype, CTYPE); #endif #ifdef LC_MESSAGES PREPARE_ONE_LOCALE (messages, MESSAGES); #endif if (failed) { int saved_err = gpg_error_from_syserror (); if (new_lc_ctype) free (new_lc_ctype); if (new_lc_messages) free (new_lc_messages); return TRACE_ERR (saved_err); } #define SET_ONE_LOCALE(lcat, ucat) \ if (category == LC_ALL || category == LC_ ## ucat) \ { \ if (ctx) \ { \ if (ctx->lc_ ## lcat) \ free (ctx->lc_ ## lcat); \ ctx->lc_ ## lcat = new_lc_ ## lcat; \ } \ else \ { \ if (def_lc_ ## lcat) \ free (def_lc_ ## lcat); \ def_lc_ ## lcat = new_lc_ ## lcat; \ } \ } if (!ctx) LOCK (def_lc_lock); #ifdef LC_CTYPE SET_ONE_LOCALE (ctype, CTYPE); #endif #ifdef LC_MESSAGES SET_ONE_LOCALE (messages, MESSAGES); #endif if (!ctx) UNLOCK (def_lc_lock); return TRACE_ERR (0); } /* Get the information about the configured engines. A pointer to the first engine in the statically allocated linked list is returned. The returned data is valid until the next gpgme_ctx_set_engine_info. */ gpgme_engine_info_t gpgme_ctx_get_engine_info (gpgme_ctx_t ctx) { TRACE (DEBUG_CTX, "gpgme_ctx_get_engine_info", ctx, "ctx->engine_info=%p", ctx->engine_info); return ctx->engine_info; } /* Set the engine info for the context CTX, protocol PROTO, to the file name FILE_NAME and the home directory HOME_DIR. */ gpgme_error_t gpgme_ctx_set_engine_info (gpgme_ctx_t ctx, gpgme_protocol_t proto, const char *file_name, const char *home_dir) { gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_ctx_set_engine_info", ctx, "protocol=%i (%s), file_name=%s, home_dir=%s", proto, gpgme_get_protocol_name (proto) ? gpgme_get_protocol_name (proto) : "unknown", file_name ? file_name : "(default)", home_dir ? home_dir : "(default)"); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); /* Shut down the engine when changing engine info. */ if (ctx->engine) { TRACE_LOG ("releasing ctx->engine=%p", ctx->engine); _gpgme_engine_release (ctx->engine); ctx->engine = NULL; } err = _gpgme_set_engine_info (ctx->engine_info, proto, file_name, home_dir); return TRACE_ERR (err); } /* Clear all notation data from the context. */ void _gpgme_sig_notation_clear (gpgme_ctx_t ctx) { gpgme_sig_notation_t notation; if (!ctx) return; notation = ctx->sig_notations; while (notation) { gpgme_sig_notation_t next_notation = notation->next; _gpgme_sig_notation_free (notation); notation = next_notation; } ctx->sig_notations = NULL; } void gpgme_sig_notation_clear (gpgme_ctx_t ctx) { TRACE (DEBUG_CTX, "gpgme_sig_notation_clear", ctx, ""); if (!ctx) return; _gpgme_sig_notation_clear (ctx); } /* Add the human-readable notation data with name NAME and value VALUE to the context CTX, using the flags FLAGS. If NAME is NULL, then VALUE should be a policy URL. The flag GPGME_SIG_NOTATION_HUMAN_READABLE is forced to be true for notation data, and false for policy URLs. */ gpgme_error_t gpgme_sig_notation_add (gpgme_ctx_t ctx, const char *name, const char *value, gpgme_sig_notation_flags_t flags) { gpgme_error_t err; gpgme_sig_notation_t notation; gpgme_sig_notation_t *lastp; TRACE_BEG (DEBUG_CTX, "gpgme_sig_notation_add", ctx, "name=%s, value=%s, flags=0x%x", name ? name : "(null)", value ? value : "(null)", flags); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (name) flags |= GPGME_SIG_NOTATION_HUMAN_READABLE; else flags &= ~GPGME_SIG_NOTATION_HUMAN_READABLE; err = _gpgme_sig_notation_create (¬ation, name, name ? strlen (name) : 0, value, value ? strlen (value) : 0, flags); if (err) return TRACE_ERR (err); lastp = &ctx->sig_notations; while (*lastp) lastp = &(*lastp)->next; *lastp = notation; return TRACE_ERR (0); } /* Get the sig notations for this context. */ gpgme_sig_notation_t gpgme_sig_notation_get (gpgme_ctx_t ctx) { if (!ctx) { TRACE (DEBUG_CTX, "gpgme_sig_notation_get", ctx, ""); return NULL; } TRACE (DEBUG_CTX, "gpgme_sig_notation_get", ctx, "ctx->sig_notations=%p", ctx->sig_notations); return ctx->sig_notations; } /* Return a public key algorithm string made of the algorithm and size or the curve name. May return NULL on error. Caller must free the result using gpgme_free. */ char * gpgme_pubkey_algo_string (gpgme_subkey_t subkey) { const char *prefix = NULL; char *result; if (!subkey) { gpg_err_set_errno (EINVAL); return NULL; } switch (subkey->pubkey_algo) { case GPGME_PK_RSA: case GPGME_PK_RSA_E: case GPGME_PK_RSA_S: prefix = "rsa"; break; case GPGME_PK_ELG_E: prefix = "elg"; break; case GPGME_PK_DSA: prefix = "dsa"; break; case GPGME_PK_ELG: prefix = "xxx"; break; case GPGME_PK_ECC: case GPGME_PK_ECDH: case GPGME_PK_ECDSA: case GPGME_PK_EDDSA: prefix = ""; break; } if (prefix && *prefix) { char buffer[40]; snprintf (buffer, sizeof buffer, "%s%u", prefix, subkey->length); result = strdup (buffer); } else if (prefix && subkey->curve && *subkey->curve) result = strdup (subkey->curve); else if (prefix) result = strdup ("E_error"); else result = strdup ("unknown"); return result; } const char * gpgme_pubkey_algo_name (gpgme_pubkey_algo_t algo) { switch (algo) { case GPGME_PK_RSA: return "RSA"; case GPGME_PK_RSA_E: return "RSA-E"; case GPGME_PK_RSA_S: return "RSA-S"; case GPGME_PK_ELG_E: return "ELG-E"; case GPGME_PK_DSA: return "DSA"; case GPGME_PK_ECC: return "ECC"; case GPGME_PK_ELG: return "ELG"; case GPGME_PK_ECDSA: return "ECDSA"; case GPGME_PK_ECDH: return "ECDH"; case GPGME_PK_EDDSA: return "EdDSA"; default: return NULL; } } const char * gpgme_hash_algo_name (gpgme_hash_algo_t algo) { switch (algo) { case GPGME_MD_MD5: return "MD5"; case GPGME_MD_SHA1: return "SHA1"; case GPGME_MD_RMD160: return "RIPEMD160"; case GPGME_MD_MD2: return "MD2"; case GPGME_MD_TIGER: return "TIGER192"; case GPGME_MD_HAVAL: return "HAVAL"; case GPGME_MD_SHA256: return "SHA256"; case GPGME_MD_SHA384: return "SHA384"; case GPGME_MD_SHA512: return "SHA512"; case GPGME_MD_SHA224: return "SHA224"; case GPGME_MD_MD4: return "MD4"; case GPGME_MD_CRC32: return "CRC32"; case GPGME_MD_CRC32_RFC1510: return "CRC32RFC1510"; case GPGME_MD_CRC24_RFC2440: return "CRC24RFC2440"; default: return NULL; } } diff --git a/src/keylist.c b/src/keylist.c index 7ea7b267..cdb115fd 100644 --- a/src/keylist.c +++ b/src/keylist.c @@ -1,1343 +1,1344 @@ /* keylist.c - Listing keys. * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007, * 2008, 2009 g10 Code GmbH * * This file is part of GPGME. * * GPGME 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. * * GPGME 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-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #ifdef HAVE_SYS_TYPES_H /* Solaris 8 needs sys/types.h before time.h. */ # include #endif #include #include #include #include #include /* Suppress warning for accessing deprecated member "class". */ #define _GPGME_IN_GPGME #include "gpgme.h" #include "util.h" #include "context.h" #include "ops.h" #include "debug.h" struct key_queue_item_s { struct key_queue_item_s *next; gpgme_key_t key; }; typedef struct { struct _gpgme_op_keylist_result result; /* The error code from ERROR keydb_search. */ gpgme_error_t keydb_search_err; gpgme_key_t tmp_key; /* This points to the last uid in tmp_key. */ gpgme_user_id_t tmp_uid; /* This points to the last sig in tmp_uid. */ gpgme_key_sig_t tmp_keysig; /* Something new is available. */ int key_cond; struct key_queue_item_s *key_queue; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; struct key_queue_item_s *key = opd->key_queue; if (opd->tmp_key) gpgme_key_unref (opd->tmp_key); /* opd->tmp_uid and opd->tmp_keysig are actually part of opd->tmp_key, so we do not need to release them here. */ while (key) { struct key_queue_item_s *next = key->next; gpgme_key_unref (key->key); key = next; } } gpgme_keylist_result_t gpgme_op_keylist_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_result", ctx, ""); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL); opd = hook; if (err || !opd) { TRACE_SUC ("result=(null)"); return NULL; } TRACE_LOG ("truncated = %i", opd->result.truncated); TRACE_SUC ("result=%p", &opd->result); return &opd->result; } static gpgme_error_t keylist_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; (void)args; err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_TRUNCATED: opd->result.truncated = 1; break; case GPGME_STATUS_ERROR: err = _gpgme_parse_failure (args); if (!opd->keydb_search_err && !strcmp (args, "keydb_search")) opd->keydb_search_err = err; err = 0; break; default: break; } return err; } static void set_subkey_trust_info (gpgme_subkey_t subkey, const char *src) { while (*src && !isdigit (*src)) { switch (*src) { case 'e': subkey->expired = 1; break; case 'r': subkey->revoked = 1; break; case 'd': /* Note that gpg 1.3 won't print that anymore but only uses the capabilities field. */ subkey->disabled = 1; break; case 'i': subkey->invalid = 1; break; } src++; } } static void set_mainkey_trust_info (gpgme_key_t key, const char *src) { /* First set the trust info of the main key (the first subkey). */ set_subkey_trust_info (key->subkeys, src); /* Now set the summarized trust info. */ while (*src && !isdigit (*src)) { switch (*src) { case 'e': key->expired = 1; break; case 'r': key->revoked = 1; break; case 'd': /* Note that gpg 1.3 won't print that anymore but only uses the capabilities field. However, it is still used for external key listings. */ key->disabled = 1; break; case 'i': key->invalid = 1; break; } src++; } } static void set_userid_flags (gpgme_key_t key, const char *src) { gpgme_user_id_t uid = key->_last_uid; assert (uid); /* Look at letters and stop at the first digit. */ while (*src && !isdigit (*src)) { switch (*src) { case 'r': uid->revoked = 1; break; case 'i': uid->invalid = 1; break; case 'n': uid->validity = GPGME_VALIDITY_NEVER; break; case 'm': uid->validity = GPGME_VALIDITY_MARGINAL; break; case 'f': uid->validity = GPGME_VALIDITY_FULL; break; case 'u': uid->validity = GPGME_VALIDITY_ULTIMATE; break; } src++; } } static void set_subkey_capability (gpgme_subkey_t subkey, const char *src) { while (*src) { switch (*src) { case 'e': subkey->can_encrypt = 1; break; case 's': subkey->can_sign = 1; break; case 'c': subkey->can_certify = 1; break; case 'a': subkey->can_authenticate = 1; break; case 'q': subkey->is_qualified = 1; break; case 'd': subkey->disabled = 1; break; } src++; } } static void set_mainkey_capability (gpgme_key_t key, const char *src) { /* First set the capabilities of the main key (the first subkey). */ set_subkey_capability (key->subkeys, src); while (*src) { switch (*src) { case 'd': case 'D': /* Note, that this flag is also set using the key validity field for backward compatibility with gpg 1.2. We use d and D, so that a future gpg version will be able to disable certain subkeys. Currently it is expected that gpg sets this for the primary key. */ key->disabled = 1; break; case 'e': case 'E': key->can_encrypt = 1; break; case 's': case 'S': key->can_sign = 1; break; case 'c': case 'C': key->can_certify = 1; break; case 'a': case 'A': key->can_authenticate = 1; break; case 'q': case 'Q': key->is_qualified = 1; break; } src++; } } static void set_ownertrust (gpgme_key_t key, const char *src) { /* Look at letters and stop at the first digit. */ while (*src && !isdigit (*src)) { switch (*src) { case 'n': key->owner_trust = GPGME_VALIDITY_NEVER; break; case 'm': key->owner_trust = GPGME_VALIDITY_MARGINAL; break; case 'f': key->owner_trust = GPGME_VALIDITY_FULL; break; case 'u': key->owner_trust = GPGME_VALIDITY_ULTIMATE; break; default: key->owner_trust = GPGME_VALIDITY_UNKNOWN; break; } src++; } } static gpgme_keyorg_t parse_keyorg (const char *string) { switch (atoi (string)) { case 0: return GPGME_KEYORG_UNKNOWN; case 1: case 2: return GPGME_KEYORG_KS; case 3: return GPGME_KEYORG_DANE; case 4: return GPGME_KEYORG_WKD; case 5: return GPGME_KEYORG_URL; case 6: return GPGME_KEYORG_FILE; case 7: return GPGME_KEYORG_SELF; default: return GPGME_KEYORG_OTHER; } } /* Parse field 15 of a secret key or subkey. This fields holds a reference to smartcards. FIELD is the content of the field and we are allowed to modify it. */ static gpg_error_t parse_sec_field15 (gpgme_key_t key, gpgme_subkey_t subkey, char *field) { if (!*field) ; /* Empty. */ else if (*field == '#') { /* This is a stub for an offline key. We reset the SECRET flag of the subkey here. Note that the secret flag of the entire key will be true even then. We even explicitly set key->secret to make it works for GPGME_KEYLIST_MODE_WITH_SECRET. */ subkey->secret = 0; key->secret = 1; } else if (strchr ("01234567890ABCDEFabcdef", *field)) { /* Fields starts with a hex digit; thus it is a serial number. */ key->secret = 1; subkey->is_cardkey = 1; subkey->card_number = strdup (field); if (!subkey->card_number) return gpg_error_from_syserror (); } else if (*field == '+') { key->secret = 1; subkey->secret = 1; } else { /* RFU. */ } return 0; } /* Parse a tfs record. */ static gpg_error_t parse_tfs_record (gpgme_user_id_t uid, char **field, int nfield) { gpg_error_t err; gpgme_tofu_info_t ti; unsigned long uval; /* We add only the first TOFU record in case future versions emit * several. */ if (uid->tofu) return 0; /* Check that we have enough fields and that the version is supported. */ if (nfield < 8 || atoi(field[1]) != 1) return trace_gpg_error (GPG_ERR_INV_ENGINE); ti = calloc (1, sizeof *ti); if (!ti) return gpg_error_from_syserror (); /* Note that we allow a value of up to 7 which is what we can store * in the ti->validity. */ err = _gpgme_strtoul_field (field[2], &uval); if (err || uval > 7) goto inv_engine; ti->validity = uval; /* Parse the sign-count. */ err = _gpgme_strtoul_field (field[3], &uval); if (err) goto inv_engine; if (uval > USHRT_MAX) uval = USHRT_MAX; ti->signcount = uval; /* Parse the encr-count. */ err = _gpgme_strtoul_field (field[4], &uval); if (err) goto inv_engine; if (uval > USHRT_MAX) uval = USHRT_MAX; ti->encrcount = uval; /* Parse the policy. */ if (!strcmp (field[5], "none")) ti->policy = GPGME_TOFU_POLICY_NONE; else if (!strcmp (field[5], "auto")) ti->policy = GPGME_TOFU_POLICY_AUTO; else if (!strcmp (field[5], "good")) ti->policy = GPGME_TOFU_POLICY_GOOD; else if (!strcmp (field[5], "bad")) ti->policy = GPGME_TOFU_POLICY_BAD; else if (!strcmp (field[5], "ask")) ti->policy = GPGME_TOFU_POLICY_ASK; else /* "unknown" and invalid policy strings. */ ti->policy = GPGME_TOFU_POLICY_UNKNOWN; /* Parse first and last seen timestamps. */ err = _gpgme_strtoul_field (field[6], &uval); if (err) goto inv_engine; ti->signfirst = uval; err = _gpgme_strtoul_field (field[7], &uval); if (err) goto inv_engine; ti->signlast = uval; if (nfield > 9) { /* This condition is only to allow for gpg 2.1.15 - can * eventually be removed. */ err = _gpgme_strtoul_field (field[8], &uval); if (err) goto inv_engine; ti->encrfirst = uval; err = _gpgme_strtoul_field (field[9], &uval); if (err) goto inv_engine; ti->encrlast = uval; } /* Ready. */ uid->tofu = ti; return 0; inv_engine: free (ti); return trace_gpg_error (GPG_ERR_INV_ENGINE); } /* We have read an entire key into tmp_key and should now finish it. It is assumed that this releases tmp_key. */ static void finish_key (gpgme_ctx_t ctx, op_data_t opd) { gpgme_key_t key = opd->tmp_key; opd->tmp_key = NULL; opd->tmp_uid = NULL; opd->tmp_keysig = NULL; if (key) _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_KEY, key); } /* Note: We are allowed to modify LINE. */ static gpgme_error_t keylist_colon_handler (void *priv, char *line) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; enum { RT_NONE, RT_SIG, RT_UID, RT_TFS, RT_SUB, RT_PUB, RT_FPR, RT_GRP, RT_SSB, RT_SEC, RT_CRT, RT_CRS, RT_REV, RT_SPK } rectype = RT_NONE; #define NR_FIELDS 20 char *field[NR_FIELDS]; int fields = 0; void *hook; op_data_t opd; gpgme_error_t err; gpgme_key_t key; gpgme_subkey_t subkey = NULL; gpgme_key_sig_t keysig = NULL; err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL); opd = hook; if (err) return err; key = opd->tmp_key; TRACE (DEBUG_CTX, "gpgme:keylist_colon_handler", ctx, "key = %p, line = %s", key, line ? line : "(null)"); if (!line) { /* End Of File. */ finish_key (ctx, opd); return 0; } while (line && fields < NR_FIELDS) { field[fields++] = line; line = strchr (line, ':'); if (line) *(line++) = '\0'; } if (!strcmp (field[0], "sig")) rectype = RT_SIG; else if (!strcmp (field[0], "rev")) rectype = RT_REV; else if (!strcmp (field[0], "pub")) rectype = RT_PUB; else if (!strcmp (field[0], "sec")) rectype = RT_SEC; else if (!strcmp (field[0], "crt")) rectype = RT_CRT; else if (!strcmp (field[0], "crs")) rectype = RT_CRS; else if (!strcmp (field[0], "fpr") && key) rectype = RT_FPR; else if (!strcmp (field[0], "grp") && key) rectype = RT_GRP; else if (!strcmp (field[0], "uid") && key) rectype = RT_UID; else if (!strcmp (field[0], "tfs") && key) rectype = RT_TFS; else if (!strcmp (field[0], "sub") && key) rectype = RT_SUB; else if (!strcmp (field[0], "ssb") && key) rectype = RT_SSB; else if (!strcmp (field[0], "spk") && key) rectype = RT_SPK; else rectype = RT_NONE; /* Only look at signature and trust info records immediately following a user ID. For this, clear the user ID pointer when encountering anything but a signature or trust record. */ if (rectype != RT_SIG && rectype != RT_REV && rectype != RT_TFS) opd->tmp_uid = NULL; /* Only look at subpackets immediately following a signature. For this, clear the signature pointer when encountering anything but a subpacket. */ if (rectype != RT_SPK) opd->tmp_keysig = NULL; switch (rectype) { case RT_PUB: case RT_SEC: case RT_CRT: case RT_CRS: /* Start a new keyblock. */ err = _gpgme_key_new (&key); if (err) return err; key->keylist_mode = ctx->keylist_mode; err = _gpgme_key_add_subkey (key, &subkey); if (err) { gpgme_key_unref (key); return err; } if (rectype == RT_SEC || rectype == RT_CRS) key->secret = subkey->secret = 1; if (rectype == RT_CRT || rectype == RT_CRS) key->protocol = GPGME_PROTOCOL_CMS; finish_key (ctx, opd); opd->tmp_key = key; /* Field 2 has the trust info. */ if (fields >= 2) set_mainkey_trust_info (key, field[1]); /* Field 3 has the key length. */ if (fields >= 3) { int i = atoi (field[2]); /* Ignore invalid values. */ if (i > 1) subkey->length = i; } /* Field 4 has the public key algorithm. */ if (fields >= 4) { int i = atoi (field[3]); if (i >= 1 && i < 128) subkey->pubkey_algo = _gpgme_map_pk_algo (i, ctx->protocol); } /* Field 5 has the long keyid. Allow short key IDs for the output of an external keyserver listing. */ if (fields >= 5 && strlen (field[4]) <= DIM(subkey->_keyid) - 1) strcpy (subkey->_keyid, field[4]); /* Field 6 has the timestamp (seconds). */ if (fields >= 6) subkey->timestamp = _gpgme_parse_timestamp (field[5], NULL); /* Field 7 has the expiration time (seconds). */ if (fields >= 7) subkey->expires = _gpgme_parse_timestamp (field[6], NULL); /* Field 8 has the X.509 serial number. */ if (fields >= 8 && (rectype == RT_CRT || rectype == RT_CRS)) { key->issuer_serial = strdup (field[7]); if (!key->issuer_serial) return gpg_error_from_syserror (); } /* Field 9 has the ownertrust. */ if (fields >= 9) set_ownertrust (key, field[8]); /* Field 10 is not used for gpg due to --fixed-list-mode option but GPGSM stores the issuer name. */ if (fields >= 10 && (rectype == RT_CRT || rectype == RT_CRS)) if (_gpgme_decode_c_string (field[9], &key->issuer_name, 0)) return gpg_error (GPG_ERR_ENOMEM); /* FIXME */ /* Field 11 has the signature class. */ /* Field 12 has the capabilities. */ if (fields >= 12) set_mainkey_capability (key, field[11]); /* Field 15 carries special flags of a secret key. */ if (fields >= 15 && (key->secret || (ctx->keylist_mode & GPGME_KEYLIST_MODE_WITH_SECRET))) { err = parse_sec_field15 (key, subkey, field[14]); if (err) return err; } /* Field 17 has the curve name for ECC. */ if (fields >= 17 && *field[16]) { subkey->curve = strdup (field[16]); if (!subkey->curve) return gpg_error_from_syserror (); } /* Field 18 has the compliance flags. */ if (fields >= 17 && *field[17]) PARSE_COMPLIANCE_FLAGS (field[17], subkey); if (fields >= 20) { key->last_update = _gpgme_parse_timestamp_ul (field[18]); key->origin = parse_keyorg (field[19]); } break; case RT_SUB: case RT_SSB: /* Start a new subkey. */ err = _gpgme_key_add_subkey (key, &subkey); if (err) return err; if (rectype == RT_SSB) subkey->secret = 1; /* Field 2 has the trust info. */ if (fields >= 2) set_subkey_trust_info (subkey, field[1]); /* Field 3 has the key length. */ if (fields >= 3) { int i = atoi (field[2]); /* Ignore invalid values. */ if (i > 1) subkey->length = i; } /* Field 4 has the public key algorithm. */ if (fields >= 4) { int i = atoi (field[3]); if (i >= 1 && i < 128) subkey->pubkey_algo = _gpgme_map_pk_algo (i, ctx->protocol); } /* Field 5 has the long keyid. */ if (fields >= 5 && strlen (field[4]) == DIM(subkey->_keyid) - 1) strcpy (subkey->_keyid, field[4]); /* Field 6 has the timestamp (seconds). */ if (fields >= 6) subkey->timestamp = _gpgme_parse_timestamp (field[5], NULL); /* Field 7 has the expiration time (seconds). */ if (fields >= 7) subkey->expires = _gpgme_parse_timestamp (field[6], NULL); /* Field 8 is reserved (LID). */ /* Field 9 has the ownertrust. */ /* Field 10, the user ID, is n/a for a subkey. */ /* Field 11 has the signature class. */ /* Field 12 has the capabilities. */ if (fields >= 12) set_subkey_capability (subkey, field[11]); /* Field 15 carries special flags of a secret key. */ if (fields >= 15 && (key->secret || (ctx->keylist_mode & GPGME_KEYLIST_MODE_WITH_SECRET))) { err = parse_sec_field15 (key, subkey, field[14]); if (err) return err; } /* Field 17 has the curve name for ECC. */ if (fields >= 17 && *field[16]) { subkey->curve = strdup (field[16]); if (!subkey->curve) return gpg_error_from_syserror (); } /* Field 18 has the compliance flags. */ if (fields >= 17 && *field[17]) PARSE_COMPLIANCE_FLAGS (field[17], subkey); break; case RT_UID: /* Field 2 has the trust info, and field 10 has the user ID. */ if (fields >= 10) { if (_gpgme_key_append_name (key, field[9], 1)) return gpg_error (GPG_ERR_ENOMEM); /* FIXME */ if (field[1]) set_userid_flags (key, field[1]); opd->tmp_uid = key->_last_uid; if (fields >= 20) { opd->tmp_uid->last_update = _gpgme_parse_timestamp_ul (field[18]); opd->tmp_uid->origin = parse_keyorg (field[19]); } } break; case RT_TFS: if (opd->tmp_uid) { err = parse_tfs_record (opd->tmp_uid, field, fields); if (err) return err; } break; case RT_FPR: /* Field 10 has the fingerprint (take only the first one). */ if (fields >= 10 && field[9] && *field[9]) { /* Need to apply it to the last subkey because all subkeys do have fingerprints. */ subkey = key->_last_subkey; if (!subkey->fpr) { subkey->fpr = strdup (field[9]); if (!subkey->fpr) return gpg_error_from_syserror (); } /* If this is the first subkey, store the fingerprint also in the KEY object. */ if (subkey == key->subkeys) { if (key->fpr && strcmp (key->fpr, subkey->fpr)) { /* FPR already set but mismatch: Should never happen. */ return trace_gpg_error (GPG_ERR_INTERNAL); } if (!key->fpr) { key->fpr = strdup (subkey->fpr); if (!key->fpr) return gpg_error_from_syserror (); } } } /* Field 13 has the gpgsm chain ID (take only the first one). */ if (fields >= 13 && !key->chain_id && *field[12]) { key->chain_id = strdup (field[12]); if (!key->chain_id) return gpg_error_from_syserror (); } break; case RT_GRP: /* Field 10 has the keygrip. */ if (fields >= 10 && field[9] && *field[9]) { /* Need to apply it to the last subkey because all subkeys have a keygrip. */ subkey = key->_last_subkey; if (!subkey->keygrip) { subkey->keygrip = strdup (field[9]); if (!subkey->keygrip) return gpg_error_from_syserror (); } } break; case RT_SIG: case RT_REV: if (!opd->tmp_uid) return 0; /* Start a new (revoked) signature. */ assert (opd->tmp_uid == key->_last_uid); keysig = _gpgme_key_add_sig (key, (fields >= 10) ? field[9] : NULL); if (!keysig) return gpg_error (GPG_ERR_ENOMEM); /* FIXME */ /* Field 2 has the calculated trust ('!', '-', '?', '%'). */ if (fields >= 2) switch (field[1][0]) { case '!': keysig->status = gpg_error (GPG_ERR_NO_ERROR); break; case '-': keysig->status = gpg_error (GPG_ERR_BAD_SIGNATURE); break; case '?': keysig->status = gpg_error (GPG_ERR_NO_PUBKEY); break; case '%': keysig->status = gpg_error (GPG_ERR_GENERAL); break; default: keysig->status = gpg_error (GPG_ERR_NO_ERROR); break; } /* Field 4 has the public key algorithm. */ if (fields >= 4) { int i = atoi (field[3]); if (i >= 1 && i < 128) keysig->pubkey_algo = _gpgme_map_pk_algo (i, ctx->protocol); } /* Field 5 has the long keyid. */ if (fields >= 5 && strlen (field[4]) == DIM(keysig->_keyid) - 1) strcpy (keysig->_keyid, field[4]); /* Field 6 has the timestamp (seconds). */ if (fields >= 6) keysig->timestamp = _gpgme_parse_timestamp (field[5], NULL); /* Field 7 has the expiration time (seconds). */ if (fields >= 7) keysig->expires = _gpgme_parse_timestamp (field[6], NULL); /* Field 11 has the signature class (eg, 0x30 means revoked). */ if (fields >= 11) if (field[10][0] && field[10][1]) { int sig_class = _gpgme_hextobyte (field[10]); if (sig_class >= 0) { keysig->sig_class = sig_class; keysig->class = keysig->sig_class; if (sig_class == 0x30) keysig->revoked = 1; } if (field[10][2] == 'x') keysig->exportable = 1; } opd->tmp_keysig = keysig; break; case RT_SPK: if (!opd->tmp_keysig) return 0; assert (opd->tmp_keysig == key->_last_uid->_last_keysig); if (fields >= 4) { /* Field 2 has the subpacket type. */ int type = atoi (field[1]); /* Field 3 has the flags. */ int flags = atoi (field[2]); /* Field 4 has the length. */ int len = atoi (field[3]); /* Field 5 has the data. */ char *data = field[4]; /* Type 20: Notation data. */ /* Type 26: Policy URL. */ if (type == 20 || type == 26) { gpgme_sig_notation_t notation; keysig = opd->tmp_keysig; /* At this time, any error is serious. */ err = _gpgme_parse_notation (¬ation, type, flags, len, data); if (err) return err; /* Add a new notation. FIXME: Could be factored out. */ if (!keysig->notations) keysig->notations = notation; if (keysig->_last_notation) keysig->_last_notation->next = notation; keysig->_last_notation = notation; } } case RT_NONE: /* Unknown record. */ break; } return 0; } void _gpgme_op_keylist_event_cb (void *data, gpgme_event_io_t type, void *type_data) { gpgme_error_t err; gpgme_ctx_t ctx = (gpgme_ctx_t) data; gpgme_key_t key = (gpgme_key_t) type_data; void *hook; op_data_t opd; struct key_queue_item_s *q, *q2; assert (type == GPGME_EVENT_NEXT_KEY); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL); opd = hook; if (err) return; q = malloc (sizeof *q); if (!q) { gpgme_key_unref (key); /* FIXME return GPGME_Out_Of_Core; */ return; } q->key = key; q->next = NULL; /* FIXME: Use a tail pointer? */ if (!(q2 = opd->key_queue)) opd->key_queue = q; else { for (; q2->next; q2 = q2->next) ; q2->next = q; } opd->key_cond = 1; } /* Start a keylist operation within CTX, searching for keys which match PATTERN. If SECRET_ONLY is true, only secret keys are returned. */ gpgme_error_t gpgme_op_keylist_start (gpgme_ctx_t ctx, const char *pattern, int secret_only) { gpgme_error_t err; void *hook; op_data_t opd; int flags = 0; TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_start", ctx, "pattern=%s, secret_only=%i", pattern, secret_only); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_reset (ctx, 2); if (err) return TRACE_ERR (err); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return TRACE_ERR (err); _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx); err = _gpgme_engine_set_colon_line_handler (ctx->engine, keylist_colon_handler, ctx); if (err) return TRACE_ERR (err); if (ctx->offline) flags |= GPGME_ENGINE_FLAG_OFFLINE; err = _gpgme_engine_op_keylist (ctx->engine, pattern, secret_only, ctx->keylist_mode, flags); return TRACE_ERR (err); } /* Start a keylist operation within CTX, searching for keys which match PATTERN. If SECRET_ONLY is true, only secret keys are returned. */ gpgme_error_t gpgme_op_keylist_ext_start (gpgme_ctx_t ctx, const char *pattern[], int secret_only, int reserved) { gpgme_error_t err; void *hook; op_data_t opd; int flags = 0; TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_ext_start", ctx, "secret_only=%i, reserved=0x%x", secret_only, reserved); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_reset (ctx, 2); if (err) return TRACE_ERR (err); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return TRACE_ERR (err); _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx); err = _gpgme_engine_set_colon_line_handler (ctx->engine, keylist_colon_handler, ctx); if (err) return TRACE_ERR (err); if (ctx->offline) flags |= GPGME_ENGINE_FLAG_OFFLINE; err = _gpgme_engine_op_keylist_ext (ctx->engine, pattern, secret_only, reserved, ctx->keylist_mode, flags); return TRACE_ERR (err); } /* Start a keylist operation within CTX to show keys contained * in DATA. */ gpgme_error_t gpgme_op_keylist_from_data_start (gpgme_ctx_t ctx, gpgme_data_t data, int reserved) { gpgme_error_t err; void *hook; op_data_t opd; TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_from_data_start", ctx, ""); if (!ctx || !data || reserved) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_reset (ctx, 2); if (err) return TRACE_ERR (err); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return TRACE_ERR (err); _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx); err = _gpgme_engine_set_colon_line_handler (ctx->engine, keylist_colon_handler, ctx); if (err) return TRACE_ERR (err); err = _gpgme_engine_op_keylist_data (ctx->engine, data); return TRACE_ERR (err); } /* Return the next key from the keylist in R_KEY. */ gpgme_error_t gpgme_op_keylist_next (gpgme_ctx_t ctx, gpgme_key_t *r_key) { gpgme_error_t err; struct key_queue_item_s *queue_item; void *hook; op_data_t opd; TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_next", ctx, ""); if (!ctx || !r_key) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); *r_key = NULL; if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL); opd = hook; if (err) return TRACE_ERR (err); if (opd == NULL) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (!opd->key_queue) { err = _gpgme_wait_on_condition (ctx, &opd->key_cond, NULL); if (err) return TRACE_ERR (err); if (!opd->key_cond) return TRACE_ERR (opd->keydb_search_err? opd->keydb_search_err /**/ : gpg_error (GPG_ERR_EOF)); opd->key_cond = 0; assert (opd->key_queue); } queue_item = opd->key_queue; opd->key_queue = queue_item->next; if (!opd->key_queue) opd->key_cond = 0; *r_key = queue_item->key; free (queue_item); - return TRACE_SUC ("key=%p (%s)", *r_key, - ((*r_key)->subkeys && (*r_key)->subkeys->fpr) ? - (*r_key)->subkeys->fpr : "invalid"); + TRACE_SUC ("key=%p (%s)", *r_key, + ((*r_key)->subkeys && (*r_key)->subkeys->fpr) ? + (*r_key)->subkeys->fpr : "invalid"); + return 0; } /* Terminate a pending keylist operation within CTX. */ gpgme_error_t gpgme_op_keylist_end (gpgme_ctx_t ctx) { TRACE (DEBUG_CTX, "gpgme_op_keylist_end", ctx, ""); if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); return 0; } /* Get the key with the fingerprint FPR from the crypto backend. If SECRET is true, get the secret key. */ gpgme_error_t gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key, int secret) { gpgme_ctx_t listctx; gpgme_error_t err; gpgme_key_t result, key; TRACE_BEG (DEBUG_CTX, "gpgme_get_key", ctx, "fpr=%s, secret=%i", fpr, secret); if (r_key) *r_key = NULL; if (!ctx || !r_key || !fpr) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (strlen (fpr) < 8) /* We have at least a key ID. */ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); /* FIXME: We use our own context because we have to avoid the user's I/O callback handlers. */ err = gpgme_new (&listctx); if (err) return TRACE_ERR (err); { gpgme_protocol_t proto; gpgme_engine_info_t info; /* Clone the relevant state. */ proto = gpgme_get_protocol (ctx); gpgme_set_protocol (listctx, proto); gpgme_set_keylist_mode (listctx, gpgme_get_keylist_mode (ctx)); info = gpgme_ctx_get_engine_info (ctx); while (info && info->protocol != proto) info = info->next; if (info) gpgme_ctx_set_engine_info (listctx, proto, info->file_name, info->home_dir); } err = gpgme_op_keylist_start (listctx, fpr, secret); if (!err) err = gpgme_op_keylist_next (listctx, &result); if (!err) { try_next_key: err = gpgme_op_keylist_next (listctx, &key); if (gpgme_err_code (err) == GPG_ERR_EOF) err = 0; else { if (!err && result && result->subkeys && result->subkeys->fpr && key && key->subkeys && key->subkeys->fpr && !strcmp (result->subkeys->fpr, key->subkeys->fpr)) { /* The fingerprint is identical. We assume that this is the same key and don't mark it as an ambiguous. This problem may occur with corrupted keyrings and has been noticed often with gpgsm. In fact gpgsm uses a similar hack to sort out such duplicates but it can't do that while listing keys. */ gpgme_key_unref (key); goto try_next_key; } if (!err) { gpgme_key_unref (key); err = gpg_error (GPG_ERR_AMBIGUOUS_NAME); } gpgme_key_unref (result); result = NULL; } } gpgme_release (listctx); if (! err) { *r_key = result; TRACE_LOG ("key=%p (%s)", *r_key, ((*r_key)->subkeys && (*r_key)->subkeys->fpr) ? (*r_key)->subkeys->fpr : "invalid"); } return TRACE_ERR (err); } diff --git a/src/posix-io.c b/src/posix-io.c index 77ecde05..be084312 100644 --- a/src/posix-io.c +++ b/src/posix-io.c @@ -1,876 +1,877 @@ /* posix-io.c - Posix I/O functions * Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2004, 2005, 2007, 2010 g10 Code GmbH * * This file is part of GPGME. * * GPGME 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. * * GPGME 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-or-later */ #ifdef HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_STDINT_H # include #endif #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #include #ifdef HAVE_SYS_UIO_H # include #endif #include #include #ifdef USE_LINUX_GETDENTS # include # include # include #endif /*USE_LINUX_GETDENTS*/ #include "util.h" #include "priv-io.h" #include "sema.h" #include "ath.h" #include "debug.h" void _gpgme_io_subsystem_init (void) { struct sigaction act; sigaction (SIGPIPE, NULL, &act); if (act.sa_handler == SIG_DFL) { act.sa_handler = SIG_IGN; sigemptyset (&act.sa_mask); act.sa_flags = 0; sigaction (SIGPIPE, &act, NULL); } } /* Write the printable version of FD to the buffer BUF of length BUFLEN. The printable version is the representation on the command line that the child process expects. */ int _gpgme_io_fd2str (char *buf, int buflen, int fd) { return snprintf (buf, buflen, "%d", fd); } /* The table to hold notification handlers. We use a linear search and extend the table as needed. */ struct notify_table_item_s { int fd; /* -1 indicates an unused entry. */ _gpgme_close_notify_handler_t handler; void *value; }; typedef struct notify_table_item_s *notify_table_item_t; static notify_table_item_t notify_table; static size_t notify_table_size; DEFINE_STATIC_LOCK (notify_table_lock); int _gpgme_io_read (int fd, void *buffer, size_t count) { int nread; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_read", fd, "buffer=%p, count=%zu", buffer, count); do { nread = _gpgme_ath_read (fd, buffer, count); } while (nread == -1 && errno == EINTR); TRACE_LOGBUFX (buffer, nread); return TRACE_SYSRES (nread); } int _gpgme_io_write (int fd, const void *buffer, size_t count) { int nwritten; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_write", fd, "buffer=%p, count=%zu", buffer, count); TRACE_LOGBUFX (buffer, count); do { nwritten = _gpgme_ath_write (fd, buffer, count); } while (nwritten == -1 && errno == EINTR); return TRACE_SYSRES (nwritten); } int _gpgme_io_pipe (int filedes[2], int inherit_idx) { int saved_errno; int err; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_pipe", filedes, "inherit_idx=%i (GPGME uses it for %s)", inherit_idx, inherit_idx ? "reading" : "writing"); err = pipe (filedes); if (err < 0) return TRACE_SYSRES (err); /* FIXME: Should get the old flags first. */ err = fcntl (filedes[1 - inherit_idx], F_SETFD, FD_CLOEXEC); saved_errno = errno; if (err < 0) { close (filedes[0]); close (filedes[1]); } errno = saved_errno; if (err) return TRACE_SYSRES (err); - return TRACE_SUC ("read=0x%x, write=0x%x", filedes[0], filedes[1]); + TRACE_SUC ("read=0x%x, write=0x%x", filedes[0], filedes[1]); + return 0; } int _gpgme_io_close (int fd) { int res; _gpgme_close_notify_handler_t handler = NULL; void *handler_value; int idx; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd, ""); if (fd == -1) { errno = EINVAL; return TRACE_SYSRES (-1); } /* First call the notify handler. */ LOCK (notify_table_lock); for (idx=0; idx < notify_table_size; idx++) { if (notify_table[idx].fd == fd) { handler = notify_table[idx].handler; handler_value = notify_table[idx].value; notify_table[idx].handler = NULL; notify_table[idx].value = NULL; notify_table[idx].fd = -1; /* Mark slot as free. */ break; } } UNLOCK (notify_table_lock); if (handler) { TRACE_LOG ("invoking close handler %p/%p", handler, handler_value); handler (fd, handler_value); } /* Then do the close. */ res = close (fd); return TRACE_SYSRES (res); } int _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler, void *value) { int res = 0; int idx; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd, "close_handler=%p/%p", handler, value); assert (fd != -1); LOCK (notify_table_lock); for (idx=0; idx < notify_table_size; idx++) if (notify_table[idx].fd == -1) break; if (idx == notify_table_size) { /* We need to increase the size of the table. The approach we take is straightforward to minimize the risk of bugs. */ notify_table_item_t newtbl; size_t newsize = notify_table_size + 64; newtbl = calloc (newsize, sizeof *newtbl); if (!newtbl) { res = -1; goto leave; } for (idx=0; idx < notify_table_size; idx++) newtbl[idx] = notify_table[idx]; for (; idx < newsize; idx++) { newtbl[idx].fd = -1; newtbl[idx].handler = NULL; newtbl[idx].value = NULL; } free (notify_table); notify_table = newtbl; idx = notify_table_size; notify_table_size = newsize; } notify_table[idx].fd = fd; notify_table[idx].handler = handler; notify_table[idx].value = value; leave: UNLOCK (notify_table_lock); return TRACE_SYSRES (res); } int _gpgme_io_set_nonblocking (int fd) { int flags; int res; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd, ""); flags = fcntl (fd, F_GETFL, 0); if (flags == -1) return TRACE_SYSRES (-1); flags |= O_NONBLOCK; res = fcntl (fd, F_SETFL, flags); return TRACE_SYSRES (res); } #ifdef USE_LINUX_GETDENTS /* This is not declared in public headers; getdents64(2) says that we must * define it ourselves. */ struct linux_dirent64 { ino64_t d_ino; off64_t d_off; unsigned short d_reclen; unsigned char d_type; char d_name[]; }; # define DIR_BUF_SIZE 1024 #endif /*USE_LINUX_GETDENTS*/ static long int get_max_fds (void) { const char *source = NULL; long int fds = -1; int rc; /* Under Linux we can figure out the highest used file descriptor by * reading /proc/self/fd. This is in the common cases much faster * than for example doing 4096 close calls where almost all of them * will fail. * * We can't use the normal opendir/readdir/closedir interface between * fork and exec in a multi-threaded process because opendir uses * malloc and thus a mutex which may deadlock with a malloc in another * thread. However, the underlying getdents system call is safe. */ #ifdef USE_LINUX_GETDENTS { int dir_fd; char dir_buf[DIR_BUF_SIZE]; struct linux_dirent64 *dir_entry; int r, pos; const char *s; int x; dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY); if (dir_fd != -1) { for (;;) { r = syscall(SYS_getdents64, dir_fd, dir_buf, DIR_BUF_SIZE); if (r == -1) { /* Fall back to other methods. */ fds = -1; break; } if (r == 0) break; for (pos = 0; pos < r; pos += dir_entry->d_reclen) { dir_entry = (struct linux_dirent64 *) (dir_buf + pos); s = dir_entry->d_name; if (*s < '0' || *s > '9') continue; /* atoi is not guaranteed to be async-signal-safe. */ for (x = 0; *s >= '0' && *s <= '9'; s++) x = x * 10 + (*s - '0'); if (!*s && x > fds && x != dir_fd) fds = x; } } close (dir_fd); } if (fds != -1) { fds++; source = "/proc"; } } #endif /*USE_LINUX_GETDENTS*/ #ifdef RLIMIT_NOFILE if (fds == -1) { struct rlimit rl; rc = getrlimit (RLIMIT_NOFILE, &rl); if (rc == 0) { source = "RLIMIT_NOFILE"; fds = rl.rlim_max; } } #endif #ifdef RLIMIT_OFILE if (fds == -1) { struct rlimit rl; rc = getrlimit (RLIMIT_OFILE, &rl); if (rc == 0) { source = "RLIMIT_OFILE"; fds = rl.rlim_max; } } #endif #ifdef _SC_OPEN_MAX if (fds == -1) { long int scres; scres = sysconf (_SC_OPEN_MAX); if (scres >= 0) { source = "_SC_OPEN_MAX"; return scres; } } #endif #ifdef OPEN_MAX if (fds == -1) { source = "OPEN_MAX"; fds = OPEN_MAX; } #endif #if !defined(RLIMIT_NOFILE) && !defined(RLIMIT_OFILE) \ && !defined(_SC_OPEN_MAX) && !defined(OPEN_MAX) #warning "No known way to get the maximum number of file descriptors." #endif if (fds == -1) { source = "arbitrary"; /* Arbitrary limit. */ fds = 1024; } /* AIX returns INT32_MAX instead of a proper value. We assume that * this is always an error and use a more reasonable limit. */ #ifdef INT32_MAX if (fds == INT32_MAX) { source = "aix-fix"; fds = 1024; } #endif TRACE (DEBUG_SYSIO, "gpgme:max_fds", 0, "max fds=%ld (%s)", fds, source); return fds; } int _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal) { int status; pid_t ret; *r_status = 0; *r_signal = 0; do ret = _gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG); while (ret == (pid_t)(-1) && errno == EINTR); if (ret == pid) { if (WIFSIGNALED (status)) { *r_status = 4; /* Need some value here. */ *r_signal = WTERMSIG (status); } else if (WIFEXITED (status)) *r_status = WEXITSTATUS (status); else *r_status = 4; /* Oops. */ return 1; } return 0; } /* Returns 0 on success, -1 on error. */ int _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, struct spawn_fd_item_s *fd_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, pid_t *r_pid) { pid_t pid; int i; int status; int signo; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_spawn", path, "path=%s", path); i = 0; while (argv[i]) { TRACE_LOG ("argv[%2i] = %s", i, argv[i]); i++; } for (i = 0; fd_list[i].fd != -1; i++) if (fd_list[i].dup_to == -1) TRACE_LOG ("fd[%i] = 0x%x", i, fd_list[i].fd); else TRACE_LOG ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].dup_to); pid = fork (); if (pid == -1) return TRACE_SYSRES (-1); if (!pid) { /* Intermediate child to prevent zombie processes. */ if ((pid = fork ()) == 0) { /* Child. */ int max_fds = -1; int fd; int seen_stdin = 0; int seen_stdout = 0; int seen_stderr = 0; if (atfork) atfork (atforkvalue, 0); /* First close all fds which will not be inherited. If we * have closefrom(2) we first figure out the highest fd we * do not want to close, then call closefrom, and on success * use the regular code to close all fds up to the start * point of closefrom. Note that Solaris' and FreeBSD's closefrom do * not return errors. */ #ifdef HAVE_CLOSEFROM { fd = -1; for (i = 0; fd_list[i].fd != -1; i++) if (fd_list[i].fd > fd) fd = fd_list[i].fd; fd++; #if defined(__sun) || defined(__FreeBSD__) closefrom (fd); max_fds = fd; #else /*!__sun */ while ((i = closefrom (fd)) && errno == EINTR) ; if (!i || errno == EBADF) max_fds = fd; #endif /*!__sun*/ } #endif /*HAVE_CLOSEFROM*/ if (max_fds == -1) max_fds = get_max_fds (); for (fd = 0; fd < max_fds; fd++) { for (i = 0; fd_list[i].fd != -1; i++) if (fd_list[i].fd == fd) break; if (fd_list[i].fd == -1) close (fd); } /* And now dup and close those to be duplicated. */ for (i = 0; fd_list[i].fd != -1; i++) { int child_fd; int res; if (fd_list[i].dup_to != -1) child_fd = fd_list[i].dup_to; else child_fd = fd_list[i].fd; if (child_fd == 0) seen_stdin = 1; else if (child_fd == 1) seen_stdout = 1; else if (child_fd == 2) seen_stderr = 1; if (fd_list[i].dup_to == -1) continue; res = dup2 (fd_list[i].fd, fd_list[i].dup_to); if (res < 0) { #if 0 /* FIXME: The debug file descriptor is not dup'ed anyway, so we can't see this. */ TRACE_LOG ("dup2 failed in child: %s\n", strerror (errno)); #endif _exit (8); } close (fd_list[i].fd); } if (! seen_stdin || ! seen_stdout || !seen_stderr) { fd = open ("/dev/null", O_RDWR); if (fd == -1) { /* The debug file descriptor is not dup'ed, so we can't do a trace output. */ _exit (8); } /* Make sure that the process has connected stdin. */ if (! seen_stdin && fd != 0) { if (dup2 (fd, 0) == -1) _exit (8); } if (! seen_stdout && fd != 1) { if (dup2 (fd, 1) == -1) _exit (8); } if (! seen_stderr && fd != 2) { if (dup2 (fd, 2) == -1) _exit (8); } if (fd != 0 && fd != 1 && fd != 2) close (fd); } execv (path, (char *const *) argv); /* Hmm: in that case we could write a special status code to the status-pipe. */ _exit (8); /* End child. */ } if (pid == -1) _exit (1); else _exit (0); } TRACE_LOG ("waiting for child process pid=%i", pid); _gpgme_io_waitpid (pid, 1, &status, &signo); if (status) return TRACE_SYSRES (-1); for (i = 0; fd_list[i].fd != -1; i++) { if (! (flags & IOSPAWN_FLAG_NOCLOSE)) _gpgme_io_close (fd_list[i].fd); /* No handle translation. */ fd_list[i].peer_name = fd_list[i].fd; } if (r_pid) *r_pid = pid; return TRACE_SYSRES (0); } /* Select on the list of fds. Returns: -1 = error, 0 = timeout or nothing to select, > 0 = number of signaled fds. */ int _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock) { fd_set readfds; fd_set writefds; unsigned int i; int any; int max_fd; int n; int count; /* Use a 1s timeout. */ struct timeval timeout = { 1, 0 }; void *dbg_help = NULL; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_select", fds, "nfds=%zu, nonblock=%u", nfds, nonblock); FD_ZERO (&readfds); FD_ZERO (&writefds); max_fd = 0; if (nonblock) timeout.tv_sec = 0; TRACE_SEQ (dbg_help, "select on [ "); any = 0; for (i = 0; i < nfds; i++) { if (fds[i].fd == -1) continue; if (fds[i].for_read) { if (fds[i].fd >= FD_SETSIZE) { TRACE_END (dbg_help, " -BAD- ]"); gpg_err_set_errno (EMFILE); return TRACE_SYSRES (-1); } assert (!FD_ISSET (fds[i].fd, &readfds)); FD_SET (fds[i].fd, &readfds); if (fds[i].fd > max_fd) max_fd = fds[i].fd; TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd); any = 1; } else if (fds[i].for_write) { if (fds[i].fd >= FD_SETSIZE) { TRACE_END (dbg_help, " -BAD- ]"); gpg_err_set_errno (EMFILE); return TRACE_SYSRES (-1); } assert (!FD_ISSET (fds[i].fd, &writefds)); FD_SET (fds[i].fd, &writefds); if (fds[i].fd > max_fd) max_fd = fds[i].fd; TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd); any = 1; } fds[i].signaled = 0; } TRACE_END (dbg_help, "]"); if (!any) return TRACE_SYSRES (0); do { count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL, &timeout); } while (count < 0 && errno == EINTR); if (count < 0) return TRACE_SYSRES (-1); TRACE_SEQ (dbg_help, "select OK [ "); if (TRACE_ENABLED (dbg_help)) { for (i = 0; i <= max_fd; i++) { if (FD_ISSET (i, &readfds)) TRACE_ADD1 (dbg_help, "r0x%x ", i); if (FD_ISSET (i, &writefds)) TRACE_ADD1 (dbg_help, "w0x%x ", i); } TRACE_END (dbg_help, "]"); } /* The variable N is used to optimize it a little bit. */ for (n = count, i = 0; i < nfds && n; i++) { if (fds[i].fd == -1) ; else if (fds[i].for_read) { if (FD_ISSET (fds[i].fd, &readfds)) { fds[i].signaled = 1; n--; } } else if (fds[i].for_write) { if (FD_ISSET (fds[i].fd, &writefds)) { fds[i].signaled = 1; n--; } } } return TRACE_SYSRES (count); } int _gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags) { int nread; int saved_errno; struct iovec *iov; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_recvmsg", fd, "msg=%p, flags=%i", msg, flags); nread = 0; iov = msg->msg_iov; while (iov < msg->msg_iov + msg->msg_iovlen) { nread += iov->iov_len; iov++; } TRACE_LOG ("about to receive %d bytes", nread); do { nread = _gpgme_ath_recvmsg (fd, msg, flags); } while (nread == -1 && errno == EINTR); saved_errno = errno; if (nread > 0) { int nr = nread; iov = msg->msg_iov; while (nr > 0) { int len = nr > iov->iov_len ? iov->iov_len : nr; TRACE_LOGBUFX (msg->msg_iov->iov_base, len); iov++; nr -= len; } } errno = saved_errno; return TRACE_SYSRES (nread); } int _gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags) { int nwritten; struct iovec *iov; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_sendmsg", fd, "msg=%p, flags=%i", msg, flags); nwritten = 0; iov = msg->msg_iov; while (iov < msg->msg_iov + msg->msg_iovlen) { nwritten += iov->iov_len; iov++; } TRACE_LOG ("about to receive %d bytes", nwritten); iov = msg->msg_iov; while (nwritten > 0) { int len = nwritten > iov->iov_len ? iov->iov_len : nwritten; TRACE_LOGBUFX (msg->msg_iov->iov_base, len); iov++; nwritten -= len; } do { nwritten = _gpgme_ath_sendmsg (fd, msg, flags); } while (nwritten == -1 && errno == EINTR); return TRACE_SYSRES (nwritten); } int _gpgme_io_dup (int fd) { int new_fd; do new_fd = dup (fd); while (new_fd == -1 && errno == EINTR); TRACE (DEBUG_SYSIO, "_gpgme_io_dup", fd, "new fd==%i", new_fd); return new_fd; } int _gpgme_io_socket (int domain, int type, int proto) { int res; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_socket", domain, "type=%i, proto=%i", type, proto); res = socket (domain, type, proto); return TRACE_SYSRES (res); } int _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen) { int res; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_connect", fd, "addr=%p, addrlen=%i", addr, addrlen); do res = ath_connect (fd, addr, addrlen); while (res == -1 && errno == EINTR); return TRACE_SYSRES (res); } diff --git a/src/signers.c b/src/signers.c index 6a5ccd22..ac88aebb 100644 --- a/src/signers.c +++ b/src/signers.c @@ -1,118 +1,119 @@ /* signers.c - Maintain signer sets. * Copyright (C) 2001 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH * * This file is part of GPGME. * * GPGME 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. * * GPGME 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-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include "gpgme.h" #include "util.h" #include "context.h" #include "debug.h" /* Delete all signers from CTX. */ void _gpgme_signers_clear (gpgme_ctx_t ctx) { unsigned int i; if (!ctx || !ctx->signers) return; for (i = 0; i < ctx->signers_len; i++) { assert (ctx->signers[i]); gpgme_key_unref (ctx->signers[i]); ctx->signers[i] = NULL; } ctx->signers_len = 0; } void gpgme_signers_clear (gpgme_ctx_t ctx) { TRACE (DEBUG_CTX, "gpgme_signers_clear", ctx, ""); _gpgme_signers_clear (ctx); } /* Add KEY to list of signers in CTX. */ gpgme_error_t gpgme_signers_add (gpgme_ctx_t ctx, const gpgme_key_t key) { TRACE_BEG (DEBUG_CTX, "gpgme_signers_add", ctx, "key=%p (%s)", key, (key && key->subkeys && key->subkeys->fpr) ? key->subkeys->fpr : "invalid"); if (!ctx || !key) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); if (ctx->signers_len == ctx->signers_size) { gpgme_key_t *newarr; int n = ctx->signers_size + 5; int j; newarr = realloc (ctx->signers, n * sizeof (*newarr)); if (!newarr) return TRACE_ERR (gpg_error_from_syserror ()); for (j = ctx->signers_size; j < n; j++) newarr[j] = NULL; ctx->signers = newarr; ctx->signers_size = n; } gpgme_key_ref (key); ctx->signers[ctx->signers_len++] = key; - return TRACE_SUC (""); + TRACE_SUC (""); + return 0; } /* Return the number of signers in CTX. */ unsigned int gpgme_signers_count (const gpgme_ctx_t ctx) { return ctx? ctx->signers_len : 0; } /* Return the SEQth signer's key in CTX with one reference. */ gpgme_key_t gpgme_signers_enum (const gpgme_ctx_t ctx, int seq) { unsigned int seqno; if (!ctx || seq < 0) return NULL; seqno = (unsigned int) seq; if (seqno >= ctx->signers_len) return NULL; gpgme_key_ref (ctx->signers[seqno]); return ctx->signers[seqno]; }