2020-01-02 14:21:12 +01:00
|
|
|
|
/* backend-sqlite.c - SQLite based backend for keyboxd
|
2020-09-11 12:00:53 +02:00
|
|
|
|
* Copyright (C) 2019, 2020 g10 Code GmbH
|
2020-01-02 14:21:12 +01:00
|
|
|
|
*
|
|
|
|
|
* This file is part of GnuPG.
|
|
|
|
|
*
|
|
|
|
|
* GnuPG is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* GnuPG 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 General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stddef.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sqlite3.h>
|
|
|
|
|
#include <npth.h>
|
|
|
|
|
|
|
|
|
|
#include "keyboxd.h"
|
|
|
|
|
#include "../common/i18n.h"
|
|
|
|
|
#include "../common/mbox-util.h"
|
|
|
|
|
#include "backend.h"
|
|
|
|
|
#include "keybox-search-desc.h"
|
|
|
|
|
#include "keybox-defs.h" /* (for the openpgp parser) */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Our definition of the backend handle. */
|
|
|
|
|
struct backend_handle_s
|
|
|
|
|
{
|
|
|
|
|
enum database_types db_type; /* Always DB_TYPE_SQLITE. */
|
|
|
|
|
unsigned int backend_id; /* Always the id of the backend. */
|
|
|
|
|
|
|
|
|
|
char filename[1];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Definition of local request data. */
|
|
|
|
|
struct be_sqlite_local_s
|
|
|
|
|
{
|
|
|
|
|
/* The statement object of the current select command. */
|
|
|
|
|
sqlite3_stmt *select_stmt;
|
|
|
|
|
|
2020-09-22 15:42:12 +02:00
|
|
|
|
/* The column numbers for UIDNO and SUBKEY or 0. */
|
|
|
|
|
int select_col_uidno;
|
|
|
|
|
int select_col_subkey;
|
|
|
|
|
|
2020-01-02 14:21:12 +01:00
|
|
|
|
/* The search mode represented by the current select command. */
|
|
|
|
|
KeydbSearchMode select_mode;
|
|
|
|
|
|
2020-09-10 12:05:21 +02:00
|
|
|
|
/* The flags active when the select was first done. */
|
|
|
|
|
unsigned int filter_opgp : 1;
|
|
|
|
|
unsigned int filter_x509 : 1;
|
|
|
|
|
|
2020-10-05 13:22:05 +02:00
|
|
|
|
/* Flag indicating that LASTUBID has a value. */
|
|
|
|
|
unsigned int lastubid_valid : 1;
|
|
|
|
|
|
2020-09-24 10:27:19 +02:00
|
|
|
|
/* The current description index. */
|
|
|
|
|
unsigned int descidx;
|
|
|
|
|
|
2020-01-02 14:21:12 +01:00
|
|
|
|
/* The select statement has been executed with success. */
|
|
|
|
|
int select_done;
|
|
|
|
|
|
|
|
|
|
/* The last row has already been reached. */
|
|
|
|
|
int select_eof;
|
2020-10-05 13:22:05 +02:00
|
|
|
|
|
|
|
|
|
/* The last UBID found by a select; only valid if LASTUBID_VALID is
|
|
|
|
|
* set. This is required to return only one blob in case a search
|
|
|
|
|
* is done over the user id and the same user id occurs several
|
|
|
|
|
* times in a blob. */
|
|
|
|
|
unsigned char lastubid[UBID_LEN];
|
2020-01-02 14:21:12 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* The Mutex we use to protect all our SQLite calls. */
|
|
|
|
|
static npth_mutex_t database_mutex = NPTH_MUTEX_INITIALIZER;
|
|
|
|
|
/* The one and only database handle. */
|
|
|
|
|
static sqlite3 *database_hd;
|
|
|
|
|
/* A lockfile used make sure only we are accessing the database. */
|
|
|
|
|
static dotlock_t database_lock;
|
|
|
|
|
|
2020-09-25 11:52:11 +02:00
|
|
|
|
/* The version of our current database schema. */
|
|
|
|
|
#define DATABASE_VERSION 1
|
2020-01-02 14:21:12 +01:00
|
|
|
|
|
2020-09-25 11:52:11 +02:00
|
|
|
|
/* Table definitions for the database. */
|
2020-01-02 14:21:12 +01:00
|
|
|
|
static struct
|
|
|
|
|
{
|
|
|
|
|
const char *sql;
|
|
|
|
|
int special;
|
|
|
|
|
} table_definitions[] =
|
|
|
|
|
{
|
|
|
|
|
{ "PRAGMA foreign_keys = ON" },
|
|
|
|
|
|
|
|
|
|
/* Table to store config values:
|
|
|
|
|
* Standard name value pairs:
|
|
|
|
|
* dbversion = 1
|
|
|
|
|
* created = <ISO time string>
|
|
|
|
|
*/
|
|
|
|
|
{ "CREATE TABLE IF NOT EXISTS config ("
|
2020-09-25 11:52:11 +02:00
|
|
|
|
"name TEXT NOT NULL UNIQUE,"
|
2020-01-02 14:21:12 +01:00
|
|
|
|
"value TEXT NOT NULL "
|
|
|
|
|
")", 1 },
|
|
|
|
|
|
|
|
|
|
/* The actual data; either X.509 certificates or OpenPGP
|
|
|
|
|
* keyblocks. */
|
|
|
|
|
{ "CREATE TABLE IF NOT EXISTS pubkey ("
|
Spelling cleanup.
No functional changes, just fixing minor spelling issues.
---
Most of these were identified from the command line by running:
codespell \
--ignore-words-list fpr,stati,keyserver,keyservers,asign,cas,iff,ifset \
--skip '*.po,ChangeLog*,help.*.txt,*.jpg,*.eps,*.pdf,*.png,*.gpg,*.asc' \
doc g13 g10 kbx agent artwork scd tests tools am common dirmngr sm \
NEWS README README.maint TODO
Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2020-02-18 15:34:42 +01:00
|
|
|
|
/* The 20 octet truncated primary-fpr */
|
2020-01-02 14:21:12 +01:00
|
|
|
|
"ubid BLOB NOT NULL PRIMARY KEY,"
|
|
|
|
|
/* The type of the public key: 1 = openpgp, 2 = X.509. */
|
|
|
|
|
"type INTEGER NOT NULL,"
|
2020-09-10 16:41:49 +02:00
|
|
|
|
/* The Ephemeral flag as used by gpgsm. Values: 0 or 1. */
|
|
|
|
|
"ephemeral INTEGER NOT NULL DEFAULT 0,"
|
|
|
|
|
/* The Revoked flag as set by gpgsm. Values: 0 or 1. */
|
|
|
|
|
"revoked INTEGER NOT NULL DEFAULT 0,"
|
2020-01-02 14:21:12 +01:00
|
|
|
|
/* The OpenPGP keyblock or X.509 certificate. */
|
|
|
|
|
"keyblob BLOB NOT NULL"
|
|
|
|
|
")" },
|
|
|
|
|
|
|
|
|
|
/* Table with fingerprints and keyids of OpenPGP and X.509 keys.
|
|
|
|
|
* It is also used for the primary key and the X.509 fingerprint
|
|
|
|
|
* because we want to be able to use the keyid and keygrip. */
|
|
|
|
|
{ "CREATE TABLE IF NOT EXISTS fingerprint ("
|
2020-09-10 12:50:45 +02:00
|
|
|
|
/* The fingerprint, for OpenPGP either 20 octets or 32 octets;
|
|
|
|
|
* for X.509 it is the same as the UBID. */
|
2020-01-02 14:21:12 +01:00
|
|
|
|
"fpr BLOB NOT NULL PRIMARY KEY,"
|
2020-09-11 12:00:53 +02:00
|
|
|
|
/* The long keyid as a 64 bit blob. */
|
|
|
|
|
"kid BLOB NOT NULL,"
|
2020-01-02 14:21:12 +01:00
|
|
|
|
/* The keygrip for this key. */
|
|
|
|
|
"keygrip BLOB NOT NULL,"
|
2020-09-22 15:42:12 +02:00
|
|
|
|
/* 0 = primary or X.509, > 0 = subkey. Also used as
|
|
|
|
|
* order number for the keys similar to uidno. */
|
2020-01-02 14:21:12 +01:00
|
|
|
|
"subkey INTEGER NOT NULL,"
|
|
|
|
|
/* The Unique Blob ID (possibly truncated fingerprint). */
|
|
|
|
|
"ubid BLOB NOT NULL REFERENCES pubkey"
|
|
|
|
|
")" },
|
|
|
|
|
|
|
|
|
|
/* Indices for the fingerprint table. */
|
|
|
|
|
{ "CREATE INDEX IF NOT EXISTS fingerprintidx0 on fingerprint (ubid)" },
|
|
|
|
|
{ "CREATE INDEX IF NOT EXISTS fingerprintidx1 on fingerprint (fpr)" },
|
|
|
|
|
{ "CREATE INDEX IF NOT EXISTS fingerprintidx2 on fingerprint (keygrip)" },
|
|
|
|
|
|
|
|
|
|
/* Table to allow fast access via user ids or mail addresses. */
|
|
|
|
|
{ "CREATE TABLE IF NOT EXISTS userid ("
|
2020-09-10 12:50:45 +02:00
|
|
|
|
/* The full user id - for X.509 the Subject or altSubject. */
|
2020-01-02 14:21:12 +01:00
|
|
|
|
"uid TEXT NOT NULL,"
|
|
|
|
|
/* The mail address if available or NULL. */
|
|
|
|
|
"addrspec TEXT,"
|
|
|
|
|
/* The type of the public key: 1 = openpgp, 2 = X.509. */
|
|
|
|
|
"type INTEGER NOT NULL,"
|
2020-09-22 15:42:12 +02:00
|
|
|
|
/* The order number of the user id within the keyblock or
|
|
|
|
|
* certificates. For X.509 0 is reserved for the issuer, 1 the
|
|
|
|
|
* subject, 2 and up the altSubjects. For OpenPGP this starts
|
|
|
|
|
* with 1 for the first user id in the keyblock. */
|
|
|
|
|
"uidno INTEGER NOT NULL,"
|
2020-01-02 14:21:12 +01:00
|
|
|
|
/* The Unique Blob ID (possibly truncated fingerprint). */
|
|
|
|
|
"ubid BLOB NOT NULL REFERENCES pubkey"
|
|
|
|
|
")" },
|
|
|
|
|
|
|
|
|
|
/* Indices for the userid table. */
|
|
|
|
|
{ "CREATE INDEX IF NOT EXISTS userididx0 on userid (ubid)" },
|
|
|
|
|
{ "CREATE INDEX IF NOT EXISTS userididx1 on userid (uid)" },
|
2020-09-10 12:50:45 +02:00
|
|
|
|
{ "CREATE INDEX IF NOT EXISTS userididx3 on userid (addrspec)" },
|
|
|
|
|
|
|
|
|
|
/* Table to allow fast access via s/n + issuer DN (X.509 only). */
|
|
|
|
|
{ "CREATE TABLE IF NOT EXISTS issuer ("
|
|
|
|
|
/* The hex encoded S/N. */
|
|
|
|
|
"sn TEXT NOT NULL,"
|
|
|
|
|
/* The RFC2253 issuer DN. */
|
|
|
|
|
"dn TEXT NOT NULL,"
|
|
|
|
|
/* The Unique Blob ID (usually the truncated fingerprint). */
|
|
|
|
|
"ubid BLOB NOT NULL REFERENCES pubkey"
|
|
|
|
|
")" },
|
|
|
|
|
{ "CREATE INDEX IF NOT EXISTS issueridx1 on issuer (dn)" }
|
2020-01-02 14:21:12 +01:00
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2020-09-25 11:52:11 +02:00
|
|
|
|
/*-- prototypes --*/
|
|
|
|
|
static gpg_error_t get_config_value (const char *name, char **r_value);
|
|
|
|
|
static gpg_error_t set_config_value (const char *name, const char *value);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
|
|
|
|
|
|
2020-09-25 11:52:11 +02:00
|
|
|
|
|
2020-01-02 14:21:12 +01:00
|
|
|
|
/* Take a lock for accessing SQLite. */
|
|
|
|
|
static void
|
|
|
|
|
acquire_mutex (void)
|
|
|
|
|
{
|
|
|
|
|
int res = npth_mutex_lock (&database_mutex);
|
|
|
|
|
if (res)
|
|
|
|
|
log_fatal ("failed to acquire database lock: %s\n",
|
|
|
|
|
gpg_strerror (gpg_error_from_errno (res)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Release a lock. */
|
|
|
|
|
static void
|
|
|
|
|
release_mutex (void)
|
|
|
|
|
{
|
|
|
|
|
int res = npth_mutex_unlock (&database_mutex);
|
|
|
|
|
if (res)
|
|
|
|
|
log_fatal ("failed to release database db lock: %s\n",
|
|
|
|
|
gpg_strerror (gpg_error_from_errno (res)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
show_sqlstr (const char *sqlstr)
|
|
|
|
|
{
|
|
|
|
|
if (!opt.verbose)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
log_info ("(SQL: %s)\n", sqlstr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
show_sqlstmt (sqlite3_stmt *stmt)
|
|
|
|
|
{
|
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
|
|
if (!opt.verbose)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
p = sqlite3_expanded_sql (stmt);
|
|
|
|
|
if (p)
|
|
|
|
|
log_info ("(SQL: %s)\n", p);
|
|
|
|
|
sqlite3_free (p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
diag_prepare_err (int res, const char *sqlstr)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
err = gpg_error (gpg_err_code_from_sqlite (res));
|
|
|
|
|
show_sqlstr (sqlstr);
|
|
|
|
|
log_error ("error preparing SQL statement: %s\n", sqlite3_errstr (res));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
diag_bind_err (int res, sqlite3_stmt *stmt)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
err = gpg_error (gpg_err_code_from_sqlite (res));
|
|
|
|
|
show_sqlstmt (stmt);
|
|
|
|
|
log_error ("error binding a value to an SQL statement: %s\n",
|
|
|
|
|
sqlite3_errstr (res));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
diag_step_err (int res, sqlite3_stmt *stmt)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
err = gpg_error (gpg_err_code_from_sqlite (res));
|
|
|
|
|
show_sqlstmt (stmt);
|
|
|
|
|
log_error ("error executing SQL statement: %s\n", sqlite3_errstr (res));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-09-11 12:00:53 +02:00
|
|
|
|
/* We store the keyid in the database as an 8 byte blob. This
|
|
|
|
|
* function converts it from the usual u32[2] array. BUFFER is a
|
|
|
|
|
* caller provided buffer of at least 8 bytes; a pointer to that
|
|
|
|
|
* buffer is the return value. */
|
|
|
|
|
static GPGRT_INLINE unsigned char *
|
|
|
|
|
kid_from_u32 (u32 *keyid, unsigned char *buffer)
|
2020-01-02 14:21:12 +01:00
|
|
|
|
{
|
2020-09-11 12:00:53 +02:00
|
|
|
|
buffer[0] = keyid[0] >> 24;
|
|
|
|
|
buffer[1] = keyid[0] >> 16;
|
|
|
|
|
buffer[2] = keyid[0] >> 8;
|
|
|
|
|
buffer[3] = keyid[0];
|
|
|
|
|
buffer[4] = keyid[1] >> 24;
|
|
|
|
|
buffer[5] = keyid[1] >> 16;
|
|
|
|
|
buffer[6] = keyid[1] >> 8;
|
|
|
|
|
buffer[7] = keyid[1];
|
|
|
|
|
|
|
|
|
|
return buffer;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Run an SQL reset on STMT. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
run_sql_reset (sqlite3_stmt *stmt)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int res;
|
|
|
|
|
|
|
|
|
|
res = sqlite3_reset (stmt);
|
|
|
|
|
if (res)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (gpg_err_code_from_sqlite (res));
|
|
|
|
|
show_sqlstmt (stmt);
|
|
|
|
|
log_error ("error executing SQL reset: %s\n", sqlite3_errstr (res));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-09-10 12:05:21 +02:00
|
|
|
|
/* Run an SQL prepare for SQLSTR and return a statement at R_STMT. If
|
2020-10-05 13:22:05 +02:00
|
|
|
|
* EXTRA or EXTRA2 are not NULL these parts are appended to the SQL
|
|
|
|
|
* statement. */
|
2020-01-02 14:21:12 +01:00
|
|
|
|
static gpg_error_t
|
2020-10-05 13:22:05 +02:00
|
|
|
|
run_sql_prepare (const char *sqlstr, const char *extra, const char *extra2,
|
|
|
|
|
sqlite3_stmt **r_stmt)
|
2020-01-02 14:21:12 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int res;
|
2020-09-10 12:05:21 +02:00
|
|
|
|
char *buffer = NULL;
|
|
|
|
|
|
2020-10-05 13:22:05 +02:00
|
|
|
|
if (extra || extra2)
|
2020-09-10 12:05:21 +02:00
|
|
|
|
{
|
2020-10-05 13:22:05 +02:00
|
|
|
|
buffer = strconcat (sqlstr, extra?extra:"", extra2, NULL);
|
2020-09-10 12:05:21 +02:00
|
|
|
|
if (!buffer)
|
|
|
|
|
return gpg_error_from_syserror ();
|
|
|
|
|
sqlstr = buffer;
|
|
|
|
|
}
|
2020-01-02 14:21:12 +01:00
|
|
|
|
|
|
|
|
|
res = sqlite3_prepare_v2 (database_hd, sqlstr, -1, r_stmt, NULL);
|
|
|
|
|
if (res)
|
|
|
|
|
err = diag_prepare_err (res, sqlstr);
|
|
|
|
|
else
|
|
|
|
|
err = 0;
|
2020-09-10 12:05:21 +02:00
|
|
|
|
xfree (buffer);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Helper to bind a BLOB parameter to a statement. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
run_sql_bind_blob (sqlite3_stmt *stmt, int no,
|
|
|
|
|
const void *blob, size_t bloblen)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int res;
|
|
|
|
|
|
|
|
|
|
res = sqlite3_bind_blob (stmt, no, blob, bloblen, SQLITE_TRANSIENT);
|
|
|
|
|
if (res)
|
|
|
|
|
err = diag_bind_err (res, stmt);
|
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Helper to bind an INTEGER parameter to a statement. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
run_sql_bind_int (sqlite3_stmt *stmt, int no, int value)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int res;
|
|
|
|
|
|
|
|
|
|
res = sqlite3_bind_int (stmt, no, value);
|
|
|
|
|
if (res)
|
|
|
|
|
err = diag_bind_err (res, stmt);
|
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Helper to bind a string parameter to a statement. VALUE is allowed
|
|
|
|
|
* to be NULL to bind NULL. */
|
|
|
|
|
static gpg_error_t
|
2020-09-10 12:05:21 +02:00
|
|
|
|
run_sql_bind_ntext (sqlite3_stmt *stmt, int no,
|
|
|
|
|
const char *value, size_t valuelen)
|
2020-01-02 14:21:12 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int res;
|
|
|
|
|
|
2020-09-10 12:05:21 +02:00
|
|
|
|
res = sqlite3_bind_text (stmt, no, value, value? valuelen:0,
|
2020-01-02 14:21:12 +01:00
|
|
|
|
SQLITE_TRANSIENT);
|
|
|
|
|
if (res)
|
|
|
|
|
err = diag_bind_err (res, stmt);
|
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-09-10 12:05:21 +02:00
|
|
|
|
/* Helper to bind a string parameter to a statement. VALUE is allowed
|
|
|
|
|
* to be NULL to bind NULL. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
run_sql_bind_text (sqlite3_stmt *stmt, int no, const char *value)
|
|
|
|
|
{
|
|
|
|
|
return run_sql_bind_ntext (stmt, no, value, value? strlen (value):0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-01-02 14:21:12 +01:00
|
|
|
|
/* Helper to bind a string parameter to a statement. VALUE is allowed
|
|
|
|
|
* to be NULL to bind NULL. A non-NULL VALUE is clamped with percent
|
|
|
|
|
* signs. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
run_sql_bind_text_like (sqlite3_stmt *stmt, int no, const char *value)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int res;
|
|
|
|
|
char *buf;
|
|
|
|
|
|
|
|
|
|
if (!value)
|
|
|
|
|
{
|
|
|
|
|
res = sqlite3_bind_null (stmt, no);
|
|
|
|
|
buf = NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
buf = xtrymalloc (strlen (value) + 2 + 1);
|
|
|
|
|
if (!buf)
|
|
|
|
|
return gpg_error_from_syserror ();
|
|
|
|
|
*buf = '%';
|
|
|
|
|
strcpy (buf+1, value);
|
|
|
|
|
strcat (buf+1, "%");
|
|
|
|
|
res = sqlite3_bind_text (stmt, no, buf, strlen (buf), SQLITE_TRANSIENT);
|
|
|
|
|
}
|
|
|
|
|
if (res)
|
|
|
|
|
err = diag_bind_err (res, stmt);
|
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
xfree (buf);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Wrapper around sqlite3_step for use with simple functions. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
run_sql_step (sqlite3_stmt *stmt)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int res;
|
|
|
|
|
|
2020-09-23 17:25:27 +02:00
|
|
|
|
show_sqlstmt (stmt);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
res = sqlite3_step (stmt);
|
|
|
|
|
if (res != SQLITE_DONE)
|
|
|
|
|
err = diag_step_err (res, stmt);
|
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Wrapper around sqlite3_step for use with select. This version does
|
|
|
|
|
* not print diags for SQLITE_DONE or SQLITE_ROW but returns them as
|
|
|
|
|
* gpg error codes. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
run_sql_step_for_select (sqlite3_stmt *stmt)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int res;
|
|
|
|
|
|
|
|
|
|
res = sqlite3_step (stmt);
|
|
|
|
|
if (res == SQLITE_DONE || res == SQLITE_ROW)
|
|
|
|
|
err = gpg_error (gpg_err_code_from_sqlite (res));
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* SQL_OK is unexpected for a select so in this case we return
|
|
|
|
|
* the OK error code by bypassing the special mapping. */
|
|
|
|
|
if (!res)
|
|
|
|
|
err = gpg_error (GPG_ERR_SQL_OK);
|
|
|
|
|
else
|
|
|
|
|
err = gpg_error (gpg_err_code_from_sqlite (res));
|
|
|
|
|
show_sqlstmt (stmt);
|
|
|
|
|
log_error ("error running SQL step: %s\n", sqlite3_errstr (res));
|
|
|
|
|
}
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Run the simple SQL statement in SQLSTR. If UBID is not NULL this
|
2020-09-23 17:25:27 +02:00
|
|
|
|
* will be bound to ?1 in SQLSTR. This command may not be used for
|
2020-01-02 14:21:12 +01:00
|
|
|
|
* select or other command which return rows. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
run_sql_statement_bind_ubid (const char *sqlstr, const unsigned char *ubid)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
sqlite3_stmt *stmt;
|
|
|
|
|
|
2020-10-05 13:22:05 +02:00
|
|
|
|
err = run_sql_prepare (sqlstr, NULL, NULL, &stmt);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
if (ubid)
|
|
|
|
|
{
|
|
|
|
|
err = run_sql_bind_blob (stmt, 1, ubid, UBID_LEN);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = run_sql_step (stmt);
|
|
|
|
|
sqlite3_finalize (stmt);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Run the simple SQL statement in SQLSTR. This command may not be used
|
|
|
|
|
* for select or other command which return rows. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
run_sql_statement (const char *sqlstr)
|
|
|
|
|
{
|
|
|
|
|
return run_sql_statement_bind_ubid (sqlstr, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-12-18 15:25:45 +01:00
|
|
|
|
static int
|
|
|
|
|
dblock_info_cb (dotlock_t h, void *opaque, enum dotlock_reasons reason,
|
|
|
|
|
const char *format, ...)
|
|
|
|
|
{
|
|
|
|
|
ctrl_t ctrl = opaque;
|
|
|
|
|
va_list arg_ptr;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int rc = 0;
|
|
|
|
|
char tmpbuf[200];
|
|
|
|
|
|
|
|
|
|
(void)h;
|
|
|
|
|
|
|
|
|
|
if (reason == DOTLOCK_WAITING)
|
|
|
|
|
{
|
|
|
|
|
if (format)
|
|
|
|
|
{
|
|
|
|
|
va_start (arg_ptr, format);
|
|
|
|
|
gpgrt_vsnprintf (tmpbuf, sizeof tmpbuf, format, arg_ptr);
|
|
|
|
|
va_end (arg_ptr);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
*tmpbuf = 0;
|
|
|
|
|
err = kbxd_status_printf (ctrl, "NOTE", "database_open %u %s",
|
|
|
|
|
gpg_error (GPG_ERR_LOCKED), tmpbuf);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("sending status line failed: %s\n", gpg_strerror (err));
|
|
|
|
|
rc = 1; /* snprintf failed. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
Spelling cleanup.
No functional changes, just fixing minor spelling issues.
---
Most of these were identified from the command line by running:
codespell \
--ignore-words-list fpr,stati,keyserver,keyservers,asign,cas,iff,ifset \
--skip '*.po,ChangeLog*,help.*.txt,*.jpg,*.eps,*.pdf,*.png,*.gpg,*.asc' \
doc g13 g10 kbx agent artwork scd tests tools am common dirmngr sm \
NEWS README README.maint TODO
Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2020-02-18 15:34:42 +01:00
|
|
|
|
/* Create and initialize a new SQL database file if it does not
|
2020-01-02 14:21:12 +01:00
|
|
|
|
* exists; else open it and check that all required objects are
|
|
|
|
|
* available. */
|
|
|
|
|
static gpg_error_t
|
2023-12-18 15:25:45 +01:00
|
|
|
|
create_or_open_database (ctrl_t ctrl, const char *filename)
|
2020-01-02 14:21:12 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int res;
|
|
|
|
|
int idx;
|
2020-09-25 11:52:11 +02:00
|
|
|
|
char *value;
|
|
|
|
|
int dbversion;
|
|
|
|
|
int setdbversion = 0;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
|
|
|
|
|
if (database_hd)
|
|
|
|
|
return 0; /* Already initialized. */
|
|
|
|
|
|
|
|
|
|
acquire_mutex ();
|
|
|
|
|
|
|
|
|
|
/* To avoid races with other temporary instances of keyboxd trying
|
|
|
|
|
* to create or update the database, we run the database with a lock
|
|
|
|
|
* file held. */
|
2023-12-18 15:25:45 +01:00
|
|
|
|
database_lock = dotlock_create (filename, DOTLOCK_PREPARE_CREATE);
|
|
|
|
|
if (!database_lock)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
log_info ("can't allocate dotlock handle: %s\n", gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
dotlock_set_info_cb (database_lock, dblock_info_cb, ctrl);
|
|
|
|
|
database_lock = dotlock_finish_create (database_lock, filename);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!database_lock)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
/* A reason for this to fail is that the directory is not
|
|
|
|
|
* writable. However, this whole locking stuff does not make
|
|
|
|
|
* sense if this is the case. An empty non-writable directory
|
|
|
|
|
* with no database is not really useful at all. */
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
log_info ("can't allocate lock for '%s': %s\n",
|
|
|
|
|
filename, gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-18 15:25:45 +01:00
|
|
|
|
if (dotlock_take (database_lock, 10000))
|
2020-01-02 14:21:12 +01:00
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
/* This is something bad. Probably a stale lockfile. */
|
|
|
|
|
log_info ("can't lock '%s': %s\n", filename, gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Database has not yet been opened. Open or create it, make sure
|
|
|
|
|
* the tables exist, and prepare the required statements. We use
|
|
|
|
|
* our own locking instead of the more complex serialization sqlite
|
|
|
|
|
* would have to do and it avoid that we call
|
|
|
|
|
* npth_unprotect/protect. */
|
|
|
|
|
res = sqlite3_open_v2 (filename,
|
|
|
|
|
&database_hd,
|
|
|
|
|
(SQLITE_OPEN_READWRITE
|
|
|
|
|
| SQLITE_OPEN_CREATE
|
|
|
|
|
| SQLITE_OPEN_NOMUTEX),
|
|
|
|
|
NULL);
|
|
|
|
|
if (res)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (gpg_err_code_from_sqlite (res));
|
|
|
|
|
log_error ("error opening '%s': %s\n", filename, sqlite3_errstr (res));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
/* Enable extended error codes. */
|
|
|
|
|
sqlite3_extended_result_codes (database_hd, 1);
|
|
|
|
|
|
|
|
|
|
/* Create the tables if needed. */
|
|
|
|
|
for (idx=0; idx < DIM(table_definitions); idx++)
|
|
|
|
|
{
|
|
|
|
|
err = run_sql_statement (table_definitions[idx].sql);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
if (table_definitions[idx].special == 1)
|
|
|
|
|
{
|
|
|
|
|
/* Check and create dbversion etc entries. */
|
2020-09-25 11:52:11 +02:00
|
|
|
|
err = get_config_value ("dbversion", &value);
|
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
|
|
|
|
|
{
|
|
|
|
|
dbversion = 0;
|
|
|
|
|
setdbversion = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("error reading database version: %s\n",
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
err = 0;
|
|
|
|
|
dbversion = 0;
|
|
|
|
|
}
|
|
|
|
|
else if ((dbversion = atoi (value)) < 1)
|
|
|
|
|
{
|
|
|
|
|
log_error ("database version %d is not valid\n", dbversion);
|
|
|
|
|
dbversion = 0;
|
|
|
|
|
}
|
|
|
|
|
log_info ("database version: %d\n", dbversion);
|
|
|
|
|
|
|
|
|
|
xfree (value);
|
|
|
|
|
err = get_config_value ("created", &value);
|
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
|
|
|
|
|
log_info ("database created: %.50s\n", "[unknown]");
|
|
|
|
|
else if (err)
|
|
|
|
|
log_error ("error getting database creation date: %s\n",
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
else
|
|
|
|
|
log_info ("database created: %.50s\n", value);
|
|
|
|
|
|
|
|
|
|
xfree (value);
|
|
|
|
|
value = NULL;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!opt.quiet)
|
|
|
|
|
log_info (_("database '%s' created\n"), filename);
|
2020-09-25 11:52:11 +02:00
|
|
|
|
|
|
|
|
|
if (setdbversion)
|
|
|
|
|
{
|
|
|
|
|
err = set_config_value ("dbversion", STR2(DATABASE_VERSION));
|
|
|
|
|
if (!err)
|
|
|
|
|
err = set_config_value ("created", isotimestamp (gnupg_get_time ()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-01-02 14:21:12 +01:00
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("error creating database '%s': %s\n"),
|
|
|
|
|
filename, gpg_strerror (err));
|
2023-12-18 15:25:45 +01:00
|
|
|
|
if (dotlock_is_locked (database_lock))
|
|
|
|
|
dotlock_release (database_lock);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
dotlock_destroy (database_lock);
|
|
|
|
|
database_lock = NULL;
|
|
|
|
|
}
|
|
|
|
|
release_mutex ();
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Install a new resource and return a handle for that backend. */
|
|
|
|
|
gpg_error_t
|
|
|
|
|
be_sqlite_add_resource (ctrl_t ctrl, backend_handle_t *r_hd,
|
|
|
|
|
const char *filename, int readonly)
|
|
|
|
|
{
|
|
|
|
|
backend_handle_t hd;
|
|
|
|
|
|
|
|
|
|
(void)ctrl;
|
|
|
|
|
(void)readonly; /* FIXME: implement read-only mode. */
|
|
|
|
|
|
|
|
|
|
*r_hd = NULL;
|
|
|
|
|
hd = xtrycalloc (1, sizeof *hd + strlen (filename));
|
|
|
|
|
if (!hd)
|
|
|
|
|
return gpg_error_from_syserror ();
|
|
|
|
|
hd->db_type = DB_TYPE_SQLITE;
|
|
|
|
|
strcpy (hd->filename, filename);
|
|
|
|
|
hd->backend_id = be_new_backend_id ();
|
|
|
|
|
|
|
|
|
|
*r_hd = hd;
|
2023-12-18 15:25:45 +01:00
|
|
|
|
return 0;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Release the backend handle HD and all its resources. HD is not
|
|
|
|
|
* valid after a call to this function. */
|
|
|
|
|
void
|
|
|
|
|
be_sqlite_release_resource (ctrl_t ctrl, backend_handle_t hd)
|
|
|
|
|
{
|
|
|
|
|
(void)ctrl;
|
|
|
|
|
|
|
|
|
|
if (!hd)
|
|
|
|
|
return;
|
|
|
|
|
hd->db_type = DB_TYPE_NONE;
|
|
|
|
|
|
|
|
|
|
xfree (hd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Helper for be_find_request_part to initialize a sqlite request part. */
|
|
|
|
|
gpg_error_t
|
|
|
|
|
be_sqlite_init_local (backend_handle_t backend_hd, db_request_part_t part)
|
|
|
|
|
{
|
|
|
|
|
(void)backend_hd;
|
|
|
|
|
|
|
|
|
|
part->besqlite = xtrycalloc (1, sizeof *part->besqlite);
|
|
|
|
|
if (!part->besqlite)
|
|
|
|
|
return gpg_error_from_syserror ();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Release local data of a sqlite request part. */
|
|
|
|
|
void
|
|
|
|
|
be_sqlite_release_local (be_sqlite_local_t ctx)
|
|
|
|
|
{
|
|
|
|
|
if (ctx->select_stmt)
|
|
|
|
|
sqlite3_finalize (ctx->select_stmt);
|
|
|
|
|
xfree (ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-09-24 16:38:21 +02:00
|
|
|
|
gpg_error_t
|
|
|
|
|
be_sqlite_rollback (void)
|
|
|
|
|
{
|
|
|
|
|
opt.in_transaction = 0;
|
|
|
|
|
if (!opt.active_transaction)
|
|
|
|
|
return 0; /* Nothing to do. */
|
|
|
|
|
|
|
|
|
|
if (!database_hd)
|
|
|
|
|
{
|
|
|
|
|
log_error ("Warning: No database handle for global rollback\n");
|
|
|
|
|
return gpg_error (GPG_ERR_INTERNAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
opt.active_transaction = 0;
|
|
|
|
|
return run_sql_statement ("rollback");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gpg_error_t
|
|
|
|
|
be_sqlite_commit (void)
|
|
|
|
|
{
|
|
|
|
|
opt.in_transaction = 0;
|
|
|
|
|
if (!opt.active_transaction)
|
|
|
|
|
return 0; /* Nothing to do. */
|
|
|
|
|
|
|
|
|
|
if (!database_hd)
|
|
|
|
|
{
|
|
|
|
|
log_error ("Warning: No database handle for global commit\n");
|
|
|
|
|
return gpg_error (GPG_ERR_INTERNAL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
opt.active_transaction = 0;
|
|
|
|
|
return run_sql_statement ("commit");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-09-25 11:52:11 +02:00
|
|
|
|
/* Return a value from the config table. NAME most not have quotes
|
|
|
|
|
* etc. If no error is returned the caller must xfree the value
|
|
|
|
|
* stored at R_VALUE. On error NULL is stored there. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
get_config_value (const char *name, char **r_value)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
sqlite3_stmt *stmt;
|
|
|
|
|
char *sqlstr;
|
|
|
|
|
const char *s;
|
|
|
|
|
|
|
|
|
|
*r_value = NULL;
|
|
|
|
|
|
|
|
|
|
sqlstr = strconcat ("SELECT value FROM config WHERE name='", name, "'", NULL);
|
|
|
|
|
if (!sqlstr)
|
|
|
|
|
return gpg_error_from_syserror ();
|
|
|
|
|
|
2020-10-05 13:22:05 +02:00
|
|
|
|
err = run_sql_prepare (sqlstr, NULL, NULL, &stmt);
|
2020-09-25 11:52:11 +02:00
|
|
|
|
xfree (sqlstr);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
err = run_sql_step_for_select (stmt);
|
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_SQL_ROW)
|
|
|
|
|
{
|
|
|
|
|
s = sqlite3_column_text (stmt, 0);
|
|
|
|
|
*r_value = xtrystrdup (s? s : "");
|
|
|
|
|
if (!*r_value)
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
|
else if (gpg_err_code (err) == GPG_ERR_SQL_DONE)
|
|
|
|
|
err = gpg_error (GPG_ERR_NOT_FOUND);
|
|
|
|
|
else
|
|
|
|
|
log_assert (err); /* We'll never see 0 here. */
|
|
|
|
|
|
|
|
|
|
sqlite3_finalize (stmt);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Insert or update a value in the config table. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
set_config_value (const char *name, const char *value)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
sqlite3_stmt *stmt;
|
|
|
|
|
|
|
|
|
|
err = run_sql_prepare ("INSERT OR REPLACE INTO config(name,value)"
|
2020-10-05 13:22:05 +02:00
|
|
|
|
" VALUES(?1,?2)", NULL, NULL, &stmt);
|
2020-09-25 11:52:11 +02:00
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
err = run_sql_bind_text (stmt, 1, name);
|
|
|
|
|
if (!err)
|
|
|
|
|
err = run_sql_bind_text (stmt, 2, value);
|
|
|
|
|
if (!err)
|
|
|
|
|
err = run_sql_step (stmt);
|
|
|
|
|
|
|
|
|
|
sqlite3_finalize (stmt);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-01-02 14:21:12 +01:00
|
|
|
|
/* Run a select for the search given by (DESC,NDESC). The data is not
|
|
|
|
|
* returned but stored in the request item. */
|
|
|
|
|
static gpg_error_t
|
2020-09-10 12:05:21 +02:00
|
|
|
|
run_select_statement (ctrl_t ctrl, be_sqlite_local_t ctx,
|
2020-01-02 14:21:12 +01:00
|
|
|
|
KEYDB_SEARCH_DESC *desc, unsigned int ndesc)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err = 0;
|
|
|
|
|
unsigned int descidx;
|
2020-09-10 12:05:21 +02:00
|
|
|
|
const char *extra = NULL;
|
2020-09-11 12:00:53 +02:00
|
|
|
|
unsigned char kidbuf[8];
|
2021-04-21 14:31:57 +02:00
|
|
|
|
const char *s;
|
|
|
|
|
size_t n;
|
2020-09-10 12:05:21 +02:00
|
|
|
|
|
2020-01-02 14:21:12 +01:00
|
|
|
|
|
2020-09-24 10:27:19 +02:00
|
|
|
|
descidx = ctx->descidx;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (descidx >= ndesc)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_EOF);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-13 00:09:23 +02:00
|
|
|
|
/* Check whether we can reuse the current select statement. */
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!ctx->select_stmt)
|
|
|
|
|
;
|
|
|
|
|
else if (ctx->select_mode != desc[descidx].mode)
|
|
|
|
|
{
|
|
|
|
|
sqlite3_finalize (ctx->select_stmt);
|
|
|
|
|
ctx->select_stmt = NULL;
|
|
|
|
|
}
|
2020-09-10 12:05:21 +02:00
|
|
|
|
else if (ctx->filter_opgp != ctrl->filter_opgp
|
|
|
|
|
|| ctx->filter_x509 != ctrl->filter_x509)
|
|
|
|
|
{
|
|
|
|
|
/* The filter flags changed, thus we can't reuse the statement. */
|
|
|
|
|
sqlite3_finalize (ctx->select_stmt);
|
|
|
|
|
ctx->select_stmt = NULL;
|
|
|
|
|
}
|
2020-01-02 14:21:12 +01:00
|
|
|
|
|
|
|
|
|
ctx->select_mode = desc[descidx].mode;
|
2020-09-10 12:05:21 +02:00
|
|
|
|
ctx->filter_opgp = ctrl->filter_opgp;
|
|
|
|
|
ctx->filter_x509 = ctrl->filter_x509;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
|
|
|
|
|
/* Prepare the select and bind the parameters. */
|
|
|
|
|
if (ctx->select_stmt)
|
|
|
|
|
{
|
|
|
|
|
err = run_sql_reset (ctx->select_stmt);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
else
|
2020-09-10 12:05:21 +02:00
|
|
|
|
{
|
|
|
|
|
if (ctx->filter_opgp && ctx->filter_x509)
|
|
|
|
|
extra = " AND ( p.type = 1 OR p.type = 2 )";
|
|
|
|
|
else if (ctx->filter_opgp && !ctx->filter_x509)
|
|
|
|
|
extra = " AND p.type = 1";
|
|
|
|
|
else if (!ctx->filter_opgp && ctx->filter_x509)
|
|
|
|
|
extra = " AND p.type = 2";
|
|
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-02 14:21:12 +01:00
|
|
|
|
|
2020-09-22 15:42:12 +02:00
|
|
|
|
ctx->select_col_uidno = ctx->select_col_subkey = 0;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
switch (desc[descidx].mode)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_SEARCH_MODE_NONE:
|
|
|
|
|
never_reached ();
|
|
|
|
|
err = gpg_error (GPG_ERR_INTERNAL);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KEYDB_SEARCH_MODE_EXACT:
|
2020-09-22 15:42:12 +02:00
|
|
|
|
ctx->select_col_uidno = 5;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!ctx->select_stmt)
|
2020-09-10 16:41:49 +02:00
|
|
|
|
err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral, p.revoked,"
|
2020-09-22 15:42:12 +02:00
|
|
|
|
" p.keyblob, u.uidno"
|
2020-01-02 14:21:12 +01:00
|
|
|
|
" FROM pubkey as p, userid as u"
|
2020-09-02 14:50:59 +02:00
|
|
|
|
" WHERE p.ubid = u.ubid AND u.uid = ?1",
|
2020-10-05 13:22:05 +02:00
|
|
|
|
extra, " ORDER BY p.ubid", &ctx->select_stmt);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!err)
|
|
|
|
|
err = run_sql_bind_text (ctx->select_stmt, 1, desc[descidx].u.name);
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_SEARCH_MODE_MAIL:
|
2020-09-22 15:42:12 +02:00
|
|
|
|
ctx->select_col_uidno = 5;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!ctx->select_stmt)
|
2020-09-10 16:41:49 +02:00
|
|
|
|
err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral, p.revoked,"
|
2020-09-22 15:42:12 +02:00
|
|
|
|
" p.keyblob, u.uidno"
|
2020-01-02 14:21:12 +01:00
|
|
|
|
" FROM pubkey as p, userid as u"
|
2020-09-02 14:50:59 +02:00
|
|
|
|
" WHERE p.ubid = u.ubid AND u.addrspec = ?1",
|
2020-10-05 13:22:05 +02:00
|
|
|
|
extra, " ORDER BY p.ubid", &ctx->select_stmt);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!err)
|
2021-04-21 14:31:57 +02:00
|
|
|
|
{
|
|
|
|
|
s = desc[descidx].u.name;
|
|
|
|
|
if (s && *s == '<' && s[1])
|
|
|
|
|
{ /* It is common that the indicator for exact addrspec
|
|
|
|
|
* search has not been removed. We do this here. */
|
|
|
|
|
s++;
|
|
|
|
|
n = strlen (s);
|
|
|
|
|
if (n > 1 && s[n-1] == '>')
|
|
|
|
|
n--;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
n = s? strlen (s):0;
|
|
|
|
|
err = run_sql_bind_ntext (ctx->select_stmt, 1, s, n);
|
|
|
|
|
}
|
2020-01-02 14:21:12 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KEYDB_SEARCH_MODE_MAILSUB:
|
2020-09-22 15:42:12 +02:00
|
|
|
|
ctx->select_col_uidno = 5;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!ctx->select_stmt)
|
2020-09-10 16:41:49 +02:00
|
|
|
|
err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral, p.revoked,"
|
2020-09-22 15:42:12 +02:00
|
|
|
|
" p.keyblob, u.uidno"
|
2020-01-02 14:21:12 +01:00
|
|
|
|
" FROM pubkey as p, userid as u"
|
2020-09-02 14:50:59 +02:00
|
|
|
|
" WHERE p.ubid = u.ubid AND u.addrspec LIKE ?1",
|
2020-10-05 13:22:05 +02:00
|
|
|
|
extra, " ORDER BY p.ubid", &ctx->select_stmt);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!err)
|
|
|
|
|
err = run_sql_bind_text_like (ctx->select_stmt, 1,
|
|
|
|
|
desc[descidx].u.name);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KEYDB_SEARCH_MODE_SUBSTR:
|
2020-09-22 15:42:12 +02:00
|
|
|
|
ctx->select_col_uidno = 5;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!ctx->select_stmt)
|
2020-09-10 16:41:49 +02:00
|
|
|
|
err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral, p.revoked,"
|
2020-09-22 15:42:12 +02:00
|
|
|
|
" p.keyblob, u.uidno"
|
2020-01-02 14:21:12 +01:00
|
|
|
|
" FROM pubkey as p, userid as u"
|
2020-09-02 14:50:59 +02:00
|
|
|
|
" WHERE p.ubid = u.ubid AND u.uid LIKE ?1",
|
2020-10-05 13:22:05 +02:00
|
|
|
|
extra, " ORDER BY p.ubid", &ctx->select_stmt);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!err)
|
|
|
|
|
err = run_sql_bind_text_like (ctx->select_stmt, 1,
|
|
|
|
|
desc[descidx].u.name);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KEYDB_SEARCH_MODE_MAILEND:
|
|
|
|
|
case KEYDB_SEARCH_MODE_WORDS:
|
|
|
|
|
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KEYDB_SEARCH_MODE_ISSUER:
|
2020-09-10 12:50:45 +02:00
|
|
|
|
if (!ctx->select_stmt)
|
2020-09-10 16:41:49 +02:00
|
|
|
|
err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral, p.revoked,"
|
|
|
|
|
" p.keyblob"
|
2020-09-10 12:50:45 +02:00
|
|
|
|
" FROM pubkey as p, issuer as i"
|
|
|
|
|
" WHERE p.ubid = i.ubid"
|
|
|
|
|
" AND i.dn = $1",
|
2020-10-05 13:22:05 +02:00
|
|
|
|
extra, " ORDER BY p.ubid", &ctx->select_stmt);
|
2020-09-10 12:50:45 +02:00
|
|
|
|
if (!err)
|
|
|
|
|
err = run_sql_bind_text (ctx->select_stmt, 1,
|
|
|
|
|
desc[descidx].u.name);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KEYDB_SEARCH_MODE_ISSUER_SN:
|
2020-09-10 12:50:45 +02:00
|
|
|
|
if (!desc[descidx].snhex)
|
|
|
|
|
{
|
|
|
|
|
/* We should never get a binary S/N here. */
|
|
|
|
|
log_debug ("%s: issuer_sn with binary s/n\n", __func__);
|
|
|
|
|
err = gpg_error (GPG_ERR_INTERNAL);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!ctx->select_stmt)
|
2020-09-10 16:41:49 +02:00
|
|
|
|
err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral,"
|
|
|
|
|
" p.revoked, p.keyblob"
|
2020-09-10 12:50:45 +02:00
|
|
|
|
" FROM pubkey as p, issuer as i"
|
|
|
|
|
" WHERE p.ubid = i.ubid"
|
|
|
|
|
" AND i.sn = $1 AND i.dn = $2",
|
2020-10-05 13:22:05 +02:00
|
|
|
|
extra, " ORDER BY p.ubid",
|
|
|
|
|
&ctx->select_stmt);
|
2020-09-10 12:50:45 +02:00
|
|
|
|
if (!err)
|
|
|
|
|
err = run_sql_bind_ntext (ctx->select_stmt, 1,
|
|
|
|
|
desc[descidx].sn, desc[descidx].snlen);
|
|
|
|
|
if (!err)
|
|
|
|
|
err = run_sql_bind_text (ctx->select_stmt, 2,
|
|
|
|
|
desc[descidx].u.name);
|
|
|
|
|
}
|
2020-01-02 14:21:12 +01:00
|
|
|
|
break;
|
2020-09-10 12:50:45 +02:00
|
|
|
|
|
2020-01-02 14:21:12 +01:00
|
|
|
|
case KEYDB_SEARCH_MODE_SN:
|
|
|
|
|
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* FIXME */
|
|
|
|
|
/* if (has_sn (blob, sn_array? sn_array[n].sn : desc[n].sn, */
|
|
|
|
|
/* sn_array? sn_array[n].snlen : desc[n].snlen)) */
|
|
|
|
|
/* goto found; */
|
|
|
|
|
break;
|
2020-09-10 12:50:45 +02:00
|
|
|
|
|
2020-01-02 14:21:12 +01:00
|
|
|
|
case KEYDB_SEARCH_MODE_SUBJECT:
|
2020-09-22 15:42:12 +02:00
|
|
|
|
ctx->select_col_uidno = 5;
|
|
|
|
|
if (!ctx->select_stmt)
|
|
|
|
|
err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral, p.revoked,"
|
|
|
|
|
" p.keyblob, u.uidno"
|
|
|
|
|
" FROM pubkey as p, userid as u"
|
|
|
|
|
" WHERE p.ubid = u.ubid"
|
|
|
|
|
" AND u.uid = $1",
|
2020-10-05 13:22:05 +02:00
|
|
|
|
extra, " ORDER BY p.ubid", &ctx->select_stmt);
|
2020-09-10 12:50:45 +02:00
|
|
|
|
if (!err)
|
|
|
|
|
err = run_sql_bind_text (ctx->select_stmt, 1,
|
|
|
|
|
desc[descidx].u.name);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KEYDB_SEARCH_MODE_SHORT_KID:
|
2020-09-22 15:42:12 +02:00
|
|
|
|
ctx->select_col_subkey = 5;
|
2020-09-11 12:00:53 +02:00
|
|
|
|
if (!ctx->select_stmt)
|
|
|
|
|
err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral,"
|
2020-09-22 15:42:12 +02:00
|
|
|
|
" p.revoked, p.keyblob, f.subkey"
|
2020-09-11 12:00:53 +02:00
|
|
|
|
" FROM pubkey as p, fingerprint as f"
|
|
|
|
|
" WHERE p.ubid = f.ubid AND"
|
|
|
|
|
" substr(f.kid,5) = ?1",
|
2020-10-05 13:22:05 +02:00
|
|
|
|
extra, " ORDER BY p.ubid", &ctx->select_stmt);
|
2020-09-11 12:00:53 +02:00
|
|
|
|
if (!err)
|
|
|
|
|
err = run_sql_bind_blob (ctx->select_stmt, 1,
|
|
|
|
|
kid_from_u32 (desc[descidx].u.kid, kidbuf)+4,
|
|
|
|
|
4);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KEYDB_SEARCH_MODE_LONG_KID:
|
2020-09-22 15:42:12 +02:00
|
|
|
|
ctx->select_col_subkey = 5;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!ctx->select_stmt)
|
2020-09-10 16:41:49 +02:00
|
|
|
|
err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral,"
|
2020-09-22 15:42:12 +02:00
|
|
|
|
" p.revoked, p.keyblob, f.subkey"
|
2020-01-02 14:21:12 +01:00
|
|
|
|
" FROM pubkey as p, fingerprint as f"
|
2020-01-07 17:12:25 +01:00
|
|
|
|
" WHERE p.ubid = f.ubid AND f.kid = ?1",
|
2020-10-05 13:22:05 +02:00
|
|
|
|
extra, " ORDER BY p.ubid", &ctx->select_stmt);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!err)
|
2020-09-11 12:00:53 +02:00
|
|
|
|
err = run_sql_bind_blob (ctx->select_stmt, 1,
|
|
|
|
|
kid_from_u32 (desc[descidx].u.kid, kidbuf),
|
|
|
|
|
8);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KEYDB_SEARCH_MODE_FPR:
|
2020-09-22 15:42:12 +02:00
|
|
|
|
ctx->select_col_subkey = 5;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!ctx->select_stmt)
|
2020-09-10 16:41:49 +02:00
|
|
|
|
err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral,"
|
2020-09-22 15:42:12 +02:00
|
|
|
|
" p.revoked, p.keyblob, f.subkey"
|
2020-01-02 14:21:12 +01:00
|
|
|
|
" FROM pubkey as p, fingerprint as f"
|
2020-01-07 17:12:25 +01:00
|
|
|
|
" WHERE p.ubid = f.ubid AND f.fpr = ?1",
|
2020-10-05 13:22:05 +02:00
|
|
|
|
extra, " ORDER BY p.ubid", &ctx->select_stmt);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!err)
|
|
|
|
|
err = run_sql_bind_blob (ctx->select_stmt, 1,
|
|
|
|
|
desc[descidx].u.fpr, desc[descidx].fprlen);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KEYDB_SEARCH_MODE_KEYGRIP:
|
2020-09-22 15:42:12 +02:00
|
|
|
|
ctx->select_col_subkey = 5;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!ctx->select_stmt)
|
2020-09-10 16:41:49 +02:00
|
|
|
|
err = run_sql_prepare ("SELECT p.ubid, p.type, p.ephemeral, p.revoked,"
|
2020-09-22 15:42:12 +02:00
|
|
|
|
" p.keyblob, f.subkey"
|
2020-01-02 14:21:12 +01:00
|
|
|
|
" FROM pubkey as p, fingerprint as f"
|
2020-01-07 17:12:25 +01:00
|
|
|
|
" WHERE p.ubid = f.ubid AND f.keygrip = ?1",
|
2020-10-05 13:22:05 +02:00
|
|
|
|
extra, " ORDER BY p.ubid", &ctx->select_stmt);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!err)
|
|
|
|
|
err = run_sql_bind_blob (ctx->select_stmt, 1,
|
|
|
|
|
desc[descidx].u.grip, KEYGRIP_LEN);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KEYDB_SEARCH_MODE_UBID:
|
|
|
|
|
if (!ctx->select_stmt)
|
2020-09-10 16:41:49 +02:00
|
|
|
|
err = run_sql_prepare ("SELECT ubid, type, ephemeral, revoked, keyblob"
|
2020-09-10 12:05:21 +02:00
|
|
|
|
" FROM pubkey as p"
|
2020-01-02 14:21:12 +01:00
|
|
|
|
" WHERE ubid = ?1",
|
2020-10-05 13:22:05 +02:00
|
|
|
|
extra, NULL, &ctx->select_stmt);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!err)
|
|
|
|
|
err = run_sql_bind_blob (ctx->select_stmt, 1,
|
|
|
|
|
desc[descidx].u.ubid, UBID_LEN);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KEYDB_SEARCH_MODE_FIRST:
|
|
|
|
|
if (!ctx->select_stmt)
|
2020-09-10 12:05:21 +02:00
|
|
|
|
{
|
|
|
|
|
if (ctx->filter_opgp && ctx->filter_x509)
|
|
|
|
|
extra = " WHERE ( p.type = 1 OR p.type = 2 ) ORDER by ubid";
|
|
|
|
|
else if (ctx->filter_opgp && !ctx->filter_x509)
|
|
|
|
|
extra = " WHERE p.type = 1 ORDER by ubid";
|
|
|
|
|
else if (!ctx->filter_opgp && ctx->filter_x509)
|
|
|
|
|
extra = " WHERE p.type = 2 ORDER by ubid";
|
|
|
|
|
else
|
|
|
|
|
extra = " ORDER by ubid";
|
|
|
|
|
|
2020-09-11 12:00:53 +02:00
|
|
|
|
err = run_sql_prepare ("SELECT ubid, type, ephemeral, revoked,"
|
|
|
|
|
" keyblob"
|
2020-09-10 12:05:21 +02:00
|
|
|
|
" FROM pubkey as p",
|
2020-10-05 13:22:05 +02:00
|
|
|
|
extra, NULL, &ctx->select_stmt);
|
2020-09-10 12:05:21 +02:00
|
|
|
|
}
|
2020-01-02 14:21:12 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KEYDB_SEARCH_MODE_NEXT:
|
|
|
|
|
err = gpg_error (GPG_ERR_INTERNAL);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_VALUE);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Search for the keys described by (DESC,NDESC) and return them to
|
|
|
|
|
* the caller. BACKEND_HD is the handle for this backend and REQUEST
|
|
|
|
|
* is the current database request object. */
|
|
|
|
|
gpg_error_t
|
|
|
|
|
be_sqlite_search (ctrl_t ctrl,
|
|
|
|
|
backend_handle_t backend_hd, db_request_t request,
|
|
|
|
|
KEYDB_SEARCH_DESC *desc, unsigned int ndesc)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
db_request_part_t part;
|
|
|
|
|
be_sqlite_local_t ctx;
|
|
|
|
|
|
|
|
|
|
log_assert (backend_hd && backend_hd->db_type == DB_TYPE_SQLITE);
|
|
|
|
|
log_assert (request);
|
|
|
|
|
|
2023-12-18 15:25:45 +01:00
|
|
|
|
err = create_or_open_database (ctrl, backend_hd->filename);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
2020-01-02 14:21:12 +01:00
|
|
|
|
acquire_mutex ();
|
|
|
|
|
|
|
|
|
|
/* Find the specific request part or allocate it. */
|
|
|
|
|
err = be_find_request_part (backend_hd, request, &part);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
ctx = part->besqlite;
|
|
|
|
|
|
|
|
|
|
if (!desc)
|
|
|
|
|
{
|
|
|
|
|
/* Reset */
|
|
|
|
|
ctx->select_done = 0;
|
|
|
|
|
ctx->select_eof = 0;
|
2020-09-24 10:27:19 +02:00
|
|
|
|
ctx->descidx = 0;
|
2020-10-05 13:22:05 +02:00
|
|
|
|
ctx->lastubid_valid = 0;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
err = 0;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ctx->select_eof)
|
|
|
|
|
{
|
|
|
|
|
/* Still in EOF state. */
|
|
|
|
|
err = gpg_error (GPG_ERR_EOF);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-24 16:38:21 +02:00
|
|
|
|
/* Start a global transaction if needed. */
|
|
|
|
|
if (!opt.active_transaction && opt.in_transaction)
|
|
|
|
|
{
|
|
|
|
|
err = run_sql_statement ("begin transaction");
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
opt.active_transaction = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-09-24 10:27:19 +02:00
|
|
|
|
again:
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!ctx->select_done)
|
|
|
|
|
{
|
|
|
|
|
/* Initial search - run the select. */
|
2020-09-10 12:05:21 +02:00
|
|
|
|
err = run_select_statement (ctrl, ctx, desc, ndesc);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
ctx->select_done = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
show_sqlstmt (ctx->select_stmt);
|
|
|
|
|
|
|
|
|
|
/* SQL select succeeded - get the first or next row. */
|
|
|
|
|
err = run_sql_step_for_select (ctx->select_stmt);
|
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_SQL_ROW)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
|
|
|
|
const void *ubid, *keyblob;
|
|
|
|
|
size_t keybloblen;
|
|
|
|
|
enum pubkey_types pubkey_type;
|
2020-09-10 16:41:49 +02:00
|
|
|
|
int is_ephemeral, is_revoked;
|
2020-09-22 15:42:12 +02:00
|
|
|
|
int pk_no, uid_no;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
|
|
|
|
|
ubid = sqlite3_column_blob (ctx->select_stmt, 0);
|
|
|
|
|
n = sqlite3_column_bytes (ctx->select_stmt, 0);
|
|
|
|
|
if (!ubid || n < 0)
|
|
|
|
|
{
|
|
|
|
|
if (!ubid && sqlite3_errcode (database_hd) == SQLITE_NOMEM)
|
|
|
|
|
err = gpg_error (gpg_err_code_from_sqlite (SQLITE_NOMEM));
|
|
|
|
|
else
|
|
|
|
|
err = gpg_error (GPG_ERR_DB_CORRUPTED);
|
|
|
|
|
show_sqlstmt (ctx->select_stmt);
|
|
|
|
|
log_error ("error in returned SQL column UBID: No column (n=%d)\n",n);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
if (n != UBID_LEN)
|
|
|
|
|
{
|
|
|
|
|
show_sqlstmt (ctx->select_stmt);
|
|
|
|
|
log_error ("error in returned SQL column UBID: Bad value (n=%d)\n",n);
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_VALUE);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-05 13:22:05 +02:00
|
|
|
|
if (ctx->lastubid_valid && !memcmp (ctx->lastubid, ubid, UBID_LEN))
|
|
|
|
|
{
|
|
|
|
|
/* The search has already returned this blob and thus we may
|
|
|
|
|
* not return this again. Consider the case that we are
|
|
|
|
|
* searching for user id "foo" and a keyblock or certificate
|
|
|
|
|
* has several userids with "foo" in it (or with even a full
|
|
|
|
|
* mail address in it but with other extra parts). The code
|
|
|
|
|
* in gpg and gpgsm expects to see only a single block and
|
|
|
|
|
* not several of them. Whether the UIDNO makes any sense
|
|
|
|
|
* in this case is questionable and we ignore that because
|
|
|
|
|
* we currently are not able to return several UIDNOs. */
|
|
|
|
|
goto again;
|
|
|
|
|
}
|
|
|
|
|
memcpy (ctx->lastubid, ubid, UBID_LEN);
|
|
|
|
|
ctx->lastubid_valid = 1;
|
|
|
|
|
|
2020-01-02 14:21:12 +01:00
|
|
|
|
n = sqlite3_column_int (ctx->select_stmt, 1);
|
|
|
|
|
if (!n && sqlite3_errcode (database_hd) == SQLITE_NOMEM)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (gpg_err_code_from_sqlite (SQLITE_NOMEM));
|
|
|
|
|
show_sqlstmt (ctx->select_stmt);
|
|
|
|
|
log_error ("error in returned SQL column TYPE: %s)\n",
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
pubkey_type = n;
|
|
|
|
|
|
2020-09-10 16:41:49 +02:00
|
|
|
|
n = sqlite3_column_int (ctx->select_stmt, 2);
|
|
|
|
|
if (!n && sqlite3_errcode (database_hd) == SQLITE_NOMEM)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (gpg_err_code_from_sqlite (SQLITE_NOMEM));
|
|
|
|
|
show_sqlstmt (ctx->select_stmt);
|
|
|
|
|
log_error ("error in returned SQL column EPHEMERAL: %s)\n",
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
is_ephemeral = !!n;
|
|
|
|
|
|
|
|
|
|
n = sqlite3_column_int (ctx->select_stmt, 3);
|
|
|
|
|
if (!n && sqlite3_errcode (database_hd) == SQLITE_NOMEM)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (gpg_err_code_from_sqlite (SQLITE_NOMEM));
|
|
|
|
|
show_sqlstmt (ctx->select_stmt);
|
|
|
|
|
log_error ("error in returned SQL column REVOKED: %s)\n",
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
is_revoked = !!n;
|
|
|
|
|
|
|
|
|
|
keyblob = sqlite3_column_blob (ctx->select_stmt, 4);
|
|
|
|
|
n = sqlite3_column_bytes (ctx->select_stmt, 4);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!keyblob || n < 0)
|
|
|
|
|
{
|
|
|
|
|
if (!keyblob && sqlite3_errcode (database_hd) == SQLITE_NOMEM)
|
|
|
|
|
err = gpg_error (gpg_err_code_from_sqlite (SQLITE_NOMEM));
|
|
|
|
|
else
|
|
|
|
|
err = gpg_error (GPG_ERR_DB_CORRUPTED);
|
|
|
|
|
show_sqlstmt (ctx->select_stmt);
|
|
|
|
|
log_error ("error in returned SQL column KEYBLOB: %s\n",
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
keybloblen = n;
|
|
|
|
|
|
2020-09-22 15:42:12 +02:00
|
|
|
|
if (ctx->select_col_uidno)
|
|
|
|
|
{
|
|
|
|
|
n = sqlite3_column_int (ctx->select_stmt, ctx->select_col_uidno);
|
|
|
|
|
if (!n && sqlite3_errcode (database_hd) == SQLITE_NOMEM)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (gpg_err_code_from_sqlite (SQLITE_NOMEM));
|
|
|
|
|
show_sqlstmt (ctx->select_stmt);
|
|
|
|
|
log_error ("error in returned SQL column UIDNO: %s)\n",
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
uid_no = 0;
|
|
|
|
|
}
|
|
|
|
|
else if (n < 0)
|
|
|
|
|
uid_no = 0;
|
|
|
|
|
else
|
|
|
|
|
uid_no = n + 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
uid_no = 0;
|
|
|
|
|
|
|
|
|
|
if (ctx->select_col_subkey)
|
|
|
|
|
{
|
|
|
|
|
n = sqlite3_column_int (ctx->select_stmt, ctx->select_col_subkey);
|
|
|
|
|
if (!n && sqlite3_errcode (database_hd) == SQLITE_NOMEM)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (gpg_err_code_from_sqlite (SQLITE_NOMEM));
|
|
|
|
|
show_sqlstmt (ctx->select_stmt);
|
|
|
|
|
log_error ("error in returned SQL column SUBKEY: %s)\n",
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
else if (n < 0)
|
|
|
|
|
pk_no = 0;
|
|
|
|
|
else
|
|
|
|
|
pk_no = n + 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
pk_no = 0;
|
|
|
|
|
|
2020-09-10 16:41:49 +02:00
|
|
|
|
err = be_return_pubkey (ctrl, keyblob, keybloblen, pubkey_type,
|
2020-09-22 15:42:12 +02:00
|
|
|
|
ubid, is_ephemeral, is_revoked, uid_no, pk_no);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!err)
|
|
|
|
|
be_cache_pubkey (ctrl, ubid, keyblob, keybloblen, pubkey_type);
|
|
|
|
|
}
|
|
|
|
|
else if (gpg_err_code (err) == GPG_ERR_SQL_DONE)
|
|
|
|
|
{
|
2020-09-24 10:27:19 +02:00
|
|
|
|
if (++ctx->descidx < ndesc)
|
|
|
|
|
{
|
|
|
|
|
ctx->select_done = 0;
|
|
|
|
|
goto again;
|
|
|
|
|
}
|
2020-01-02 14:21:12 +01:00
|
|
|
|
err = gpg_error (GPG_ERR_EOF);
|
|
|
|
|
ctx->select_eof = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
log_assert (err);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
release_mutex ();
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Helper for be_sqlite_store to update or insert a row in the pubkey
|
|
|
|
|
* table. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
store_into_pubkey (enum kbxd_store_modes mode,
|
|
|
|
|
enum pubkey_types pktype, const unsigned char *ubid,
|
|
|
|
|
const void *blob, size_t bloblen)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
const char *sqlstr;
|
|
|
|
|
sqlite3_stmt *stmt = NULL;
|
|
|
|
|
|
|
|
|
|
if (mode == KBXD_STORE_UPDATE)
|
2020-09-23 17:25:27 +02:00
|
|
|
|
sqlstr = ("UPDATE pubkey set keyblob = ?3, type = ?2 WHERE ubid = ?1");
|
2020-01-02 14:21:12 +01:00
|
|
|
|
else if (mode == KBXD_STORE_INSERT)
|
2020-09-23 17:25:27 +02:00
|
|
|
|
sqlstr = ("INSERT INTO pubkey(ubid,type,keyblob) VALUES(?1,?2,?3)");
|
2020-01-02 14:21:12 +01:00
|
|
|
|
else /* Auto */
|
|
|
|
|
sqlstr = ("INSERT OR REPLACE INTO pubkey(ubid,type,keyblob)"
|
2020-09-23 17:25:27 +02:00
|
|
|
|
" VALUES(?1,?2,?3)");
|
2020-10-05 13:22:05 +02:00
|
|
|
|
err = run_sql_prepare (sqlstr, NULL, NULL, &stmt);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
err = run_sql_bind_blob (stmt, 1, ubid, UBID_LEN);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
err = run_sql_bind_int (stmt, 2, (int)pktype);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
err = run_sql_bind_blob (stmt, 3, blob, bloblen);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
err = run_sql_step (stmt);
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
if (stmt)
|
|
|
|
|
sqlite3_finalize (stmt);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Helper for be_sqlite_store to update or insert a row in the
|
|
|
|
|
* fingerprint table. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
store_into_fingerprint (const unsigned char *ubid, int subkey,
|
2020-09-11 12:00:53 +02:00
|
|
|
|
const unsigned char *keygrip,
|
|
|
|
|
const unsigned char *kid,
|
2020-01-02 14:21:12 +01:00
|
|
|
|
const unsigned char *fpr, int fprlen)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
const char *sqlstr;
|
|
|
|
|
sqlite3_stmt *stmt = NULL;
|
|
|
|
|
|
|
|
|
|
sqlstr = ("INSERT OR REPLACE INTO fingerprint(fpr,kid,keygrip,subkey,ubid)"
|
2020-09-23 17:25:27 +02:00
|
|
|
|
" VALUES(?1,?2,?3,?4,?5)");
|
2020-10-05 13:22:05 +02:00
|
|
|
|
err = run_sql_prepare (sqlstr, NULL, NULL, &stmt);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
err = run_sql_bind_blob (stmt, 1, fpr, fprlen);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
2020-09-11 12:00:53 +02:00
|
|
|
|
err = run_sql_bind_blob (stmt, 2, kid, 8);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
err = run_sql_bind_blob (stmt, 3, keygrip, KEYGRIP_LEN);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
err = run_sql_bind_int (stmt, 4, subkey);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
err = run_sql_bind_blob (stmt, 5, ubid, UBID_LEN);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
err = run_sql_step (stmt);
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
if (stmt)
|
|
|
|
|
sqlite3_finalize (stmt);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-09-10 12:50:45 +02:00
|
|
|
|
/* Helper for be_sqlite_store to update or insert a row in the userid
|
|
|
|
|
* table. If OVERRIDE_MBOX is set, that value is used instead of a
|
|
|
|
|
* value extracted from UID. */
|
2020-01-02 14:21:12 +01:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
store_into_userid (const unsigned char *ubid, enum pubkey_types pktype,
|
2020-09-22 15:42:12 +02:00
|
|
|
|
const char *uid, int uidno, const char *override_mbox)
|
2020-01-02 14:21:12 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
const char *sqlstr;
|
|
|
|
|
sqlite3_stmt *stmt = NULL;
|
|
|
|
|
char *addrspec = NULL;
|
|
|
|
|
|
2020-09-22 15:42:12 +02:00
|
|
|
|
sqlstr = ("INSERT OR REPLACE INTO userid(uid,addrspec,type,ubid,uidno)"
|
2020-09-23 17:25:27 +02:00
|
|
|
|
" VALUES(?1,?2,?3,?4,?5)");
|
2020-10-05 13:22:05 +02:00
|
|
|
|
err = run_sql_prepare (sqlstr, NULL, NULL, &stmt);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
err = run_sql_bind_text (stmt, 1, uid);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
2020-09-10 12:50:45 +02:00
|
|
|
|
|
|
|
|
|
if (override_mbox)
|
|
|
|
|
err = run_sql_bind_text (stmt, 2, override_mbox);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
addrspec = mailbox_from_userid (uid, 0);
|
|
|
|
|
err = run_sql_bind_text (stmt, 2, addrspec);
|
|
|
|
|
}
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
2020-09-10 12:50:45 +02:00
|
|
|
|
|
2020-01-02 14:21:12 +01:00
|
|
|
|
err = run_sql_bind_int (stmt, 3, pktype);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
err = run_sql_bind_blob (stmt, 4, ubid, UBID_LEN);
|
2020-09-22 15:42:12 +02:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
err = run_sql_bind_int (stmt, 5, uidno);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
err = run_sql_step (stmt);
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
if (stmt)
|
|
|
|
|
sqlite3_finalize (stmt);
|
|
|
|
|
xfree (addrspec);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-09-10 12:50:45 +02:00
|
|
|
|
/* Helper for be_sqlite_store to update or insert a row in the
|
|
|
|
|
* issuer table. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
store_into_issuer (const unsigned char *ubid,
|
|
|
|
|
const char *sn, const char *issuer)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
const char *sqlstr;
|
|
|
|
|
sqlite3_stmt *stmt = NULL;
|
|
|
|
|
char *addrspec = NULL;
|
|
|
|
|
|
|
|
|
|
sqlstr = ("INSERT OR REPLACE INTO issuer(sn,dn,ubid)"
|
2020-09-23 17:25:27 +02:00
|
|
|
|
" VALUES(?1,?2,?3)");
|
2020-10-05 13:22:05 +02:00
|
|
|
|
err = run_sql_prepare (sqlstr, NULL, NULL, &stmt);
|
2020-09-10 12:50:45 +02:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
err = run_sql_bind_text (stmt, 1, sn);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
err = run_sql_bind_text (stmt, 2, issuer);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
err = run_sql_bind_blob (stmt, 3, ubid, UBID_LEN);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
err = run_sql_step (stmt);
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
if (stmt)
|
|
|
|
|
sqlite3_finalize (stmt);
|
|
|
|
|
xfree (addrspec);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
Spelling cleanup.
No functional changes, just fixing minor spelling issues.
---
Most of these were identified from the command line by running:
codespell \
--ignore-words-list fpr,stati,keyserver,keyservers,asign,cas,iff,ifset \
--skip '*.po,ChangeLog*,help.*.txt,*.jpg,*.eps,*.pdf,*.png,*.gpg,*.asc' \
doc g13 g10 kbx agent artwork scd tests tools am common dirmngr sm \
NEWS README README.maint TODO
Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2020-02-18 15:34:42 +01:00
|
|
|
|
/* Store (BLOB,BLOBLEN) into the database. UBID is the UBID matching
|
2020-01-02 14:21:12 +01:00
|
|
|
|
* that blob. BACKEND_HD is the handle for this backend and REQUEST
|
|
|
|
|
* is the current database request object. MODE is the store
|
|
|
|
|
* mode. */
|
|
|
|
|
gpg_error_t
|
|
|
|
|
be_sqlite_store (ctrl_t ctrl, backend_handle_t backend_hd,
|
|
|
|
|
db_request_t request, enum kbxd_store_modes mode,
|
|
|
|
|
enum pubkey_types pktype, const unsigned char *ubid,
|
|
|
|
|
const void *blob, size_t bloblen)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
db_request_part_t part;
|
|
|
|
|
/* be_sqlite_local_t ctx; */
|
|
|
|
|
int got_mutex = 0;
|
|
|
|
|
int in_transaction = 0;
|
|
|
|
|
int info_valid = 0;
|
|
|
|
|
struct _keybox_openpgp_info info;
|
2020-09-10 12:50:45 +02:00
|
|
|
|
ksba_cert_t cert = NULL;
|
|
|
|
|
char *sn = NULL;
|
|
|
|
|
char *dn = NULL;
|
|
|
|
|
char *kludge_mbox = NULL;
|
2020-09-22 15:42:12 +02:00
|
|
|
|
int uidno;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
|
|
|
|
|
(void)ctrl;
|
|
|
|
|
|
|
|
|
|
log_assert (backend_hd && backend_hd->db_type == DB_TYPE_SQLITE);
|
|
|
|
|
log_assert (request);
|
|
|
|
|
|
|
|
|
|
/* Fixme: The code below is duplicated in be_ubid_from_blob - we
|
|
|
|
|
* should have only one function and pass the passed info around
|
|
|
|
|
* with the BLOB. */
|
|
|
|
|
|
|
|
|
|
if (be_is_x509_blob (blob, bloblen))
|
|
|
|
|
{
|
2020-09-10 12:50:45 +02:00
|
|
|
|
log_assert (pktype == PUBKEY_TYPE_X509);
|
|
|
|
|
|
|
|
|
|
err = ksba_cert_new (&cert);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
err = ksba_cert_init_from_mem (cert, blob, bloblen);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
err = _keybox_parse_openpgp (blob, bloblen, NULL, &info);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_info ("error parsing OpenPGP blob: %s\n", gpg_strerror (err));
|
|
|
|
|
err = gpg_error (GPG_ERR_WRONG_BLOB_TYPE);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
info_valid = 1;
|
|
|
|
|
log_assert (pktype == PUBKEY_TYPE_OPGP);
|
|
|
|
|
log_assert (info.primary.fprlen >= 20);
|
|
|
|
|
log_assert (!memcmp (ubid, info.primary.fpr, UBID_LEN));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
acquire_mutex ();
|
|
|
|
|
got_mutex = 1;
|
|
|
|
|
|
|
|
|
|
/* Find the specific request part or allocate it. */
|
|
|
|
|
err = be_find_request_part (backend_hd, request, &part);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
/* ctx = part->besqlite; */
|
|
|
|
|
|
2020-09-24 16:38:21 +02:00
|
|
|
|
if (!opt.active_transaction)
|
|
|
|
|
{
|
|
|
|
|
err = run_sql_statement ("begin transaction");
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
if (opt.in_transaction)
|
|
|
|
|
opt.active_transaction = 1;
|
|
|
|
|
}
|
2020-01-02 14:21:12 +01:00
|
|
|
|
in_transaction = 1;
|
|
|
|
|
|
|
|
|
|
err = store_into_pubkey (mode, pktype, ubid, blob, bloblen);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
/* Delete all related rows so that we can freshly add possibly added
|
|
|
|
|
* or changed user ids and subkeys. */
|
|
|
|
|
err = run_sql_statement_bind_ubid
|
2020-09-23 17:25:27 +02:00
|
|
|
|
("DELETE FROM fingerprint WHERE ubid = ?1", ubid);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
err = run_sql_statement_bind_ubid
|
2020-09-23 17:25:27 +02:00
|
|
|
|
("DELETE FROM userid WHERE ubid = ?1", ubid);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
2020-09-10 12:50:45 +02:00
|
|
|
|
if (cert)
|
|
|
|
|
{
|
|
|
|
|
err = run_sql_statement_bind_ubid
|
2020-09-23 17:25:27 +02:00
|
|
|
|
("DELETE FROM issuer WHERE ubid = ?1", ubid);
|
2020-09-10 12:50:45 +02:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2020-01-02 14:21:12 +01:00
|
|
|
|
|
2020-09-10 12:50:45 +02:00
|
|
|
|
if (cert) /* X.509 */
|
2020-01-02 14:21:12 +01:00
|
|
|
|
{
|
2020-09-10 12:50:45 +02:00
|
|
|
|
unsigned char grip[KEYGRIP_LEN];
|
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
|
|
err = be_get_x509_keygrip (cert, grip);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
/* Note that for X.509 the UBID is also the fingerprint. */
|
|
|
|
|
err = store_into_fingerprint (ubid, 0, grip,
|
2020-09-11 12:00:53 +02:00
|
|
|
|
ubid+12,
|
2020-09-10 12:50:45 +02:00
|
|
|
|
ubid, UBID_LEN);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
/* Now the issuer. */
|
|
|
|
|
sn = be_get_x509_serial (cert);
|
|
|
|
|
if (!sn)
|
2020-01-02 14:21:12 +01:00
|
|
|
|
{
|
2020-09-10 12:50:45 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
dn = ksba_cert_get_issuer (cert, 0);
|
|
|
|
|
if (!dn)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
err = store_into_issuer (ubid, sn, dn);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
/* Loop over the subject and alternate subjects. */
|
2020-09-22 15:42:12 +02:00
|
|
|
|
uidno = 0;
|
2020-09-10 12:50:45 +02:00
|
|
|
|
for (idx=0; (xfree (dn), dn = ksba_cert_get_subject (cert, idx)); idx++)
|
|
|
|
|
{
|
|
|
|
|
/* In the case that the same email address is in the
|
|
|
|
|
* subject DN as well as in an alternate subject name
|
|
|
|
|
* we avoid printing it a second time. */
|
|
|
|
|
if (kludge_mbox && !strcmp (kludge_mbox, dn))
|
|
|
|
|
continue;
|
|
|
|
|
|
2020-09-22 15:42:12 +02:00
|
|
|
|
err = store_into_userid (ubid, PUBKEY_TYPE_X509, dn, ++uidno, NULL);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
2020-09-10 12:50:45 +02:00
|
|
|
|
if (!idx)
|
|
|
|
|
{
|
|
|
|
|
kludge_mbox = _keybox_x509_email_kludge (dn);
|
|
|
|
|
if (kludge_mbox)
|
|
|
|
|
{
|
|
|
|
|
err = store_into_userid (ubid, PUBKEY_TYPE_X509,
|
2020-09-22 15:42:12 +02:00
|
|
|
|
dn, ++uidno, kludge_mbox);
|
2020-09-10 12:50:45 +02:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} /* end loop over the subjects. */
|
|
|
|
|
}
|
|
|
|
|
else /* OpenPGP */
|
2020-01-02 14:21:12 +01:00
|
|
|
|
{
|
2020-09-10 12:50:45 +02:00
|
|
|
|
struct _keybox_openpgp_key_info *kinfo;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
|
2020-09-10 12:50:45 +02:00
|
|
|
|
kinfo = &info.primary;
|
|
|
|
|
err = store_into_fingerprint (ubid, 0, kinfo->grip,
|
2020-09-11 12:00:53 +02:00
|
|
|
|
kinfo->keyid,
|
2020-09-10 12:50:45 +02:00
|
|
|
|
kinfo->fpr, kinfo->fprlen);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
if (info.nsubkeys)
|
2020-01-02 14:21:12 +01:00
|
|
|
|
{
|
2020-09-10 12:50:45 +02:00
|
|
|
|
int subkey = 1;
|
|
|
|
|
for (kinfo = &info.subkeys; kinfo; kinfo = kinfo->next, subkey++)
|
|
|
|
|
{
|
|
|
|
|
err = store_into_fingerprint (ubid, subkey, kinfo->grip,
|
2020-09-11 12:00:53 +02:00
|
|
|
|
kinfo->keyid,
|
2020-09-10 12:50:45 +02:00
|
|
|
|
kinfo->fpr, kinfo->fprlen);
|
|
|
|
|
if (err)
|
2020-01-02 14:21:12 +01:00
|
|
|
|
goto leave;
|
2020-09-10 12:50:45 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (info.nuids)
|
|
|
|
|
{
|
|
|
|
|
struct _keybox_openpgp_uid_info *u;
|
|
|
|
|
|
2020-09-22 15:42:12 +02:00
|
|
|
|
uidno = 0;
|
2020-09-10 12:50:45 +02:00
|
|
|
|
u = &info.uids;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
log_assert (u->off <= bloblen);
|
|
|
|
|
log_assert (u->off + u->len <= bloblen);
|
|
|
|
|
{
|
|
|
|
|
char *uid = xtrymalloc (u->len + 1);
|
|
|
|
|
if (!uid)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
memcpy (uid, (const unsigned char *)blob + u->off, u->len);
|
|
|
|
|
uid[u->len] = 0;
|
|
|
|
|
/* Note that we ignore embedded zeros in the user id;
|
|
|
|
|
* this is what we do all over the place. */
|
2020-09-22 15:42:12 +02:00
|
|
|
|
err = store_into_userid (ubid, pktype, uid, ++uidno, NULL);
|
2020-09-10 12:50:45 +02:00
|
|
|
|
xfree (uid);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
}
|
2020-09-10 12:50:45 +02:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
2020-01-02 14:21:12 +01:00
|
|
|
|
|
2020-09-10 12:50:45 +02:00
|
|
|
|
u = u->next;
|
|
|
|
|
}
|
|
|
|
|
while (u);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
if (in_transaction && !err)
|
2020-09-24 16:38:21 +02:00
|
|
|
|
{
|
|
|
|
|
if (opt.active_transaction)
|
|
|
|
|
; /* We are in a global transaction. */
|
|
|
|
|
else
|
|
|
|
|
err = run_sql_statement ("commit");
|
|
|
|
|
}
|
2020-01-02 14:21:12 +01:00
|
|
|
|
else if (in_transaction)
|
|
|
|
|
{
|
2020-09-24 16:38:21 +02:00
|
|
|
|
if (opt.active_transaction)
|
|
|
|
|
; /* We are in a global transaction. */
|
|
|
|
|
else if (run_sql_statement ("rollback"))
|
2020-01-02 14:21:12 +01:00
|
|
|
|
log_error ("Warning: database rollback failed - should not happen!\n");
|
|
|
|
|
}
|
|
|
|
|
if (got_mutex)
|
|
|
|
|
release_mutex ();
|
|
|
|
|
if (info_valid)
|
|
|
|
|
_keybox_destroy_openpgp_info (&info);
|
2020-09-10 12:50:45 +02:00
|
|
|
|
if (cert)
|
|
|
|
|
ksba_cert_release (cert);
|
|
|
|
|
ksba_free (dn);
|
|
|
|
|
xfree (sn);
|
|
|
|
|
xfree (kludge_mbox);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Delete the blob specified by UBID from the database. BACKEND_HD is
|
|
|
|
|
* the handle for this backend and REQUEST is the current database
|
|
|
|
|
* request object. */
|
|
|
|
|
gpg_error_t
|
|
|
|
|
be_sqlite_delete (ctrl_t ctrl, backend_handle_t backend_hd,
|
|
|
|
|
db_request_t request, const unsigned char *ubid)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
db_request_part_t part;
|
|
|
|
|
/* be_sqlite_local_t ctx; */
|
|
|
|
|
sqlite3_stmt *stmt = NULL;
|
|
|
|
|
int in_transaction = 0;
|
|
|
|
|
|
|
|
|
|
(void)ctrl;
|
|
|
|
|
|
|
|
|
|
log_assert (backend_hd && backend_hd->db_type == DB_TYPE_SQLITE);
|
|
|
|
|
log_assert (request);
|
|
|
|
|
|
|
|
|
|
acquire_mutex ();
|
|
|
|
|
|
|
|
|
|
/* Find the specific request part or allocate it. */
|
|
|
|
|
err = be_find_request_part (backend_hd, request, &part);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
/* ctx = part->besqlite; */
|
|
|
|
|
|
2020-09-24 16:38:21 +02:00
|
|
|
|
if (!opt.active_transaction)
|
|
|
|
|
{
|
|
|
|
|
err = run_sql_statement ("begin transaction");
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
if (opt.in_transaction)
|
|
|
|
|
opt.active_transaction = 1;
|
|
|
|
|
}
|
2020-01-02 14:21:12 +01:00
|
|
|
|
in_transaction = 1;
|
|
|
|
|
|
|
|
|
|
err = run_sql_statement_bind_ubid
|
2020-09-23 17:25:27 +02:00
|
|
|
|
("DELETE from userid WHERE ubid = ?1", ubid);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!err)
|
|
|
|
|
err = run_sql_statement_bind_ubid
|
2020-09-23 17:25:27 +02:00
|
|
|
|
("DELETE from fingerprint WHERE ubid = ?1", ubid);
|
2020-09-13 20:48:53 +02:00
|
|
|
|
if (!err)
|
|
|
|
|
err = run_sql_statement_bind_ubid
|
2020-09-23 17:25:27 +02:00
|
|
|
|
("DELETE from issuer WHERE ubid = ?1", ubid);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (!err)
|
|
|
|
|
err = run_sql_statement_bind_ubid
|
2020-09-23 17:25:27 +02:00
|
|
|
|
("DELETE from pubkey WHERE ubid = ?1", ubid);
|
2020-01-02 14:21:12 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
if (stmt)
|
|
|
|
|
sqlite3_finalize (stmt);
|
2020-09-24 16:38:21 +02:00
|
|
|
|
|
2020-01-02 14:21:12 +01:00
|
|
|
|
if (in_transaction && !err)
|
2020-09-24 16:38:21 +02:00
|
|
|
|
{
|
|
|
|
|
if (opt.active_transaction)
|
|
|
|
|
; /* We are in a global transaction. */
|
|
|
|
|
else
|
|
|
|
|
err = run_sql_statement ("commit");
|
|
|
|
|
}
|
2020-01-02 14:21:12 +01:00
|
|
|
|
else if (in_transaction)
|
|
|
|
|
{
|
2020-09-24 16:38:21 +02:00
|
|
|
|
if (opt.active_transaction)
|
|
|
|
|
; /* We are in a global transaction. */
|
|
|
|
|
else if (run_sql_statement ("rollback"))
|
2020-01-02 14:21:12 +01:00
|
|
|
|
log_error ("Warning: database rollback failed - should not happen!\n");
|
|
|
|
|
}
|
|
|
|
|
release_mutex ();
|
|
|
|
|
return err;
|
|
|
|
|
}
|