mirror of
git://git.gnupg.org/gnupg.git
synced 2025-07-02 22:46:30 +02:00
This commit was manufactured by cvs2svn to create branch
'GNUPG-1-9-BRANCH'.
This commit is contained in:
parent
8d76177f10
commit
82a17c9fb3
563 changed files with 0 additions and 267875 deletions
419
agent/ChangeLog
419
agent/ChangeLog
|
@ -1,419 +0,0 @@
|
|||
2002-09-25 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpg-agent.c (handle_signal): Flush cache on SIGHUP.
|
||||
* cache.c (agent_flush_cache): New.
|
||||
|
||||
* gpg-agent.c, agent.h: Add --keep-display and --keep-tty.
|
||||
* query.c (start_pinentry): Implement them. The option passing
|
||||
needs more thoughts.
|
||||
|
||||
2002-09-09 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpg-agent.c (create_private_keys_directory)
|
||||
(create_directories): New.
|
||||
(main): Try to create a home directory.
|
||||
|
||||
2002-09-04 Neal H. Walfield <neal@g10code.de>
|
||||
|
||||
* gpg-agent.c (main): Use sigaction, not signal.
|
||||
|
||||
2002-09-03 Neal H. Walfield <neal@g10code.de>
|
||||
|
||||
* findkey.c: Include <fcntl.h>.
|
||||
(agent_write_private_key): Prefer POSIX compatibity, open and
|
||||
fdopen, over the simplicity of GNU extensions, fopen(file, "x").
|
||||
|
||||
2002-08-22 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* query.c (agent_askpin): Provide the default desc text depending
|
||||
on the pininfo. Do the basic PIN verification only when
|
||||
min_digits is set.
|
||||
|
||||
2002-08-21 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* query.c (agent_askpin): Hack to show the right default prompt.
|
||||
(agent_get_passphrase): Ditto.
|
||||
|
||||
* trans.c: Removed and replaced all usages with standard _()
|
||||
|
||||
* divert-scd.c (getpin_cb): Pass a more descritive text to the
|
||||
pinentry.
|
||||
|
||||
* Makefile.am: Renamed the binary protect-tool to gpg-protect-tool.
|
||||
* protect-tool.c: Removed the note about internal use only.
|
||||
|
||||
* gpg-agent.c (main): New option --daemon so that the program is
|
||||
not accidently started in the background.
|
||||
|
||||
2002-08-16 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* call-scd.c (learn_status_cb): Handle CERTINFO status.
|
||||
(agent_card_learn): Add args for certinfo cb.
|
||||
* learncard.c (release_certinfo,certinfo_cb): New.
|
||||
(send_cert_back): New. With factored out code from ..
|
||||
(agent_handle_learn): here. Return certinfo stuff.
|
||||
|
||||
2002-07-26 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpg-agent.c (main): New option --ignore-cache-for-signing.
|
||||
* command.c (option_handler): New server option
|
||||
use-cache-for-signing defaulting to true.
|
||||
(cmd_pksign): handle global and per session option.
|
||||
* findkey.c (agent_key_from_file, unprotect): New arg
|
||||
ignore_cache. Changed all callers.
|
||||
* pksign.c (agent_pksign): Likewise.
|
||||
|
||||
2002-06-29 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* query.c (start_pinentry): Use GNUPG_DERAULT_PINENTRY.
|
||||
* call-scd.c (start_scd): Use GNUPG_DEFAULT_SCDAEMON.
|
||||
|
||||
2002-06-28 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* protect-tool.c (export_p12_file): New.
|
||||
(main): New command --p12-export.
|
||||
* minip12.c (create_final,p12_build,compute_tag_length): New.
|
||||
(store_tag_length): New.
|
||||
|
||||
2002-06-27 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* minip12.c (crypt_block): Renamed from decrypt_block, add arg to
|
||||
allow encryption.
|
||||
|
||||
* Makefile.am (pkglib_PROGRAMS): Put protect-tool there.
|
||||
|
||||
* findkey.c (agent_write_private_key,agent_key_from_file)
|
||||
(agent_key_available): Use GNUPG_PRIVATE_KEYS_DIR constant.
|
||||
* gpg-agent.c (main): Use GNUPG_DEFAULT_HOMEDIR constant.
|
||||
|
||||
* protect-tool.c (store_private_key): New.
|
||||
(import_p12_file): Store the new file if requested.
|
||||
(main): New options --force and --store.
|
||||
|
||||
* gpg-agent.c (main): Set a global flag when running detached.
|
||||
* query.c (start_pinentry): Pass the list of FD to keep in the
|
||||
child when not running detached.
|
||||
* call-scd.c (start_scd): Ditto.
|
||||
|
||||
2002-06-26 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* command.c (cmd_istrusted, cmd_listtrusted, cmd_marktrusted)
|
||||
(cmd_pksign, cmd_pkdecrypt, cmd_genkey, cmd_get_passphrase)
|
||||
(cmd_learn): Print an error message for a failed operation.
|
||||
|
||||
* simple-pwquery.c, simple-pwquery.h: New.
|
||||
* protect-tool. (get_passphrase): New, used to get a passphrase
|
||||
from the agent if none was given on the command line.
|
||||
|
||||
2002-06-25 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* protect-tool.c (rsa_key_check): New.
|
||||
(import_p12_file): New.
|
||||
(main): New command --p12-import.
|
||||
* minip12.c, minip12.h: New.
|
||||
|
||||
2002-06-24 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* protect-tool.c (read_file): New.
|
||||
(read_key): Factored most code out to read_file.
|
||||
|
||||
2002-06-17 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* agent.h: Add a callback function to the pin_entry_info structure.
|
||||
* query.c (agent_askpin): Use the callback to check for a correct
|
||||
PIN. Removed the start_err_text argument because it is not
|
||||
anymore needed; changed callers.
|
||||
* findkey.c (unprotect): Replace our own check loop by a callback.
|
||||
(try_unprotect_cb): New.
|
||||
* genkey.c (reenter_compare_cb): New.
|
||||
(agent_genkey): Use this callback here. Fixed setting of the pi2
|
||||
variable and a segv in case of an empty PIN.
|
||||
|
||||
* divert-scd.c (getpin_cb): Removed some unused stuff and
|
||||
explained what we still have to change.
|
||||
|
||||
2002-06-12 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpg-agent.c (main): New option --disable-pth.
|
||||
|
||||
2002-06-11 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* protect-tool.c: Add command --show-keygrip
|
||||
(show_keygrip): New.
|
||||
|
||||
2002-05-23 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* call-scd.c: Seirialized all scdaeom access when using Pth.
|
||||
|
||||
* cache.c: Made the cache Pth-thread-safe.
|
||||
(agent_unlock_cache_entry): New.
|
||||
* findkey.c (unprotect): Unlock the returned cache value.
|
||||
* command.c (cmd_get_passphrase): Ditto.
|
||||
|
||||
* gpg-agent.c (main): Register pth_read/write with Assuan.
|
||||
|
||||
2002-05-22 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* query.c: Serialized all pinentry access when using Pth.
|
||||
|
||||
* gpg-agent.c (handle_signal,start_connection_thread)
|
||||
(handle_connections): New
|
||||
(main): Use the new Pth stuff to allow concurrent connections.
|
||||
* command.c (start_command_handler): Add new arg FD so that the
|
||||
fucntion can also be used for an already connected socket.
|
||||
* Makefile.am: Link with Pth.
|
||||
|
||||
2002-05-14 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* cache.c (housekeeping, agent_put_cache): Use our time() wrapper.
|
||||
|
||||
2002-04-26 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* cache.c (agent_put_cache): Reinitialize the creation time and
|
||||
the ttl when reusing a slot.
|
||||
|
||||
* call-scd.c (start_scd): Print debug messages only with debug
|
||||
flags set.
|
||||
* query.c (start_pinentry): Ditto.
|
||||
|
||||
2002-04-25 Marcus Brinkmann <marcus@g10code.de>
|
||||
|
||||
* agent.h (agent_get_confirmation): Replace paramter prompt with
|
||||
two parameters ok and cancel.
|
||||
* query.c (agent_get_confirmation): Likewise. Implement this.
|
||||
* trustlist.c (agent_marktrusted): Fix invocation of
|
||||
agent_get_confirmation.
|
||||
* divert-scd.c (ask_for_card): Likewise.
|
||||
|
||||
2002-04-24 Marcus Brinkmann <marcus@g10code.de>
|
||||
|
||||
* agent.h (struct opt): Add members display, ttyname, ttytype,
|
||||
lc_ctype, and lc_messages.
|
||||
* gpg-agent.c (enum cmd_and_opt_values): Add oDisplay, oTTYname,
|
||||
oTTYtype, oLCctype, and LCmessages.
|
||||
(main): Handle these options.
|
||||
* command.c (option_handler): New function.
|
||||
(register_commands): Register option handler.
|
||||
* query.c (start_pinentry): Pass the various display and tty
|
||||
options to the pinentry.
|
||||
|
||||
2002-04-05 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* protect-tool.c (show_file): New. Used as default action.
|
||||
|
||||
2002-03-28 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* divert-scd.c (encode_md_for_card): Don't do the pkcs-1 padding,
|
||||
the scdaemon should take care of it.
|
||||
(ask_for_card): Hack to not display the trailing zero.
|
||||
|
||||
2002-03-11 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* learncard.c (kpinfo_cb): Remove the content restrictions from
|
||||
the keyID.
|
||||
|
||||
2002-03-06 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* learncard.c: New.
|
||||
* divert-scd.c (ask_for_card): The serial number is binary so
|
||||
convert it to hex here.
|
||||
* findkey.c (agent_write_private_key): New.
|
||||
* genkey.c (store_key): And use it here.
|
||||
|
||||
* pkdecrypt.c (agent_pkdecrypt): Changed the way the diversion is done.
|
||||
* divert-scd.c (divert_pkdecrypt): Changed interface and
|
||||
implemented it.
|
||||
|
||||
2002-03-05 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* call-scd.c (inq_needpin): New.
|
||||
(agent_card_pksign): Add getpin_cb args.
|
||||
(agent_card_pkdecrypt): New.
|
||||
|
||||
2002-03-04 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* pksign.c (agent_pksign): Changed how the diversion is done.
|
||||
* divert-scd.c (divert_pksign): Changed interface and implemented it.
|
||||
(encode_md_for_card): New.
|
||||
* call-scd.c (agent_card_pksign): New.
|
||||
|
||||
2002-02-28 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* pksign.c (agent_pksign): Detect whether a Smartcard is to be
|
||||
used and divert the operation in this case.
|
||||
* pkdecrypt.c (agent_pkdecrypt): Likewise
|
||||
* findkey.c (agent_key_from_file): Add optional arg shadow_info
|
||||
and have it return information about a shadowed key.
|
||||
* protect.c (agent_get_shadow_info): New.
|
||||
|
||||
* protect.c (snext,sskip,smatch): Moved to
|
||||
* sexp-parse.h: new file.
|
||||
* divert-scd.c: New.
|
||||
|
||||
2002-02-27 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* protect.c (agent_shadow_key): New.
|
||||
|
||||
* command.c (cmd_learn): New command LEARN.
|
||||
* gpg-agent.c: New option --scdaemon-program.
|
||||
* call-scd.c (start_scd): New. Based on query.c
|
||||
* query.c: Add 2 more arguments to all uses of assuan_transact.
|
||||
|
||||
2002-02-18 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* findkey.c (unprotect): Show an error message for a bad passphrase.
|
||||
|
||||
* command.c (cmd_marktrusted): Implemented.
|
||||
* trustlist.c (agent_marktrusted): New.
|
||||
(open_list): Add APPEND arg.
|
||||
|
||||
* query.c (agent_get_confirmation): New.
|
||||
|
||||
2002-02-06 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* cache.c (housekeeping): Fixed linking in the remove case.
|
||||
|
||||
2002-02-01 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpg-agent.c: New option --default-cache-ttl.
|
||||
* cache.c (agent_put_cache): Use it.
|
||||
|
||||
* cache.c: Add a few debug outputs.
|
||||
|
||||
* protect.c (agent_private_key_type): New.
|
||||
* agent.h: Add PRIVATE_KEY_ enums.
|
||||
* findkey.c (agent_key_from_file): Use it to decide whether we
|
||||
have to unprotect a key.
|
||||
(unprotect): Cache the passphrase.
|
||||
|
||||
* findkey.c (agent_key_from_file,agent_key_available): The key
|
||||
files do now require a ".key" suffix to make a script's life
|
||||
easier.
|
||||
* genkey.c (store_key): Ditto.
|
||||
|
||||
2002-01-31 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* genkey.c (store_key): Protect the key.
|
||||
(agent_genkey): Ask for the passphrase.
|
||||
* findkey.c (unprotect): Actually unprotect the key.
|
||||
* query.c (agent_askpin): Add an optional start_err_text.
|
||||
|
||||
2002-01-30 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* protect.c: New.
|
||||
(hash_passphrase): Based on the GnuPG 1.0.6 version.
|
||||
* protect-tool.c: New
|
||||
|
||||
2002-01-29 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* findkey.c (agent_key_available): New.
|
||||
* command.c (cmd_havekey): New.
|
||||
(register_commands): And register new command.
|
||||
|
||||
2002-01-20 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* command.c (cmd_get_passphrase): Remove the plus signs.
|
||||
|
||||
* query.c (start_pinentry): Send no-grab option to pinentry
|
||||
* gpg-agent.c (main): Move variable grab as no_grab to agent.h.
|
||||
|
||||
2002-01-19 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpg-agent.c (main): Disable core dumps.
|
||||
|
||||
* cache.c: New.
|
||||
* command.c (cmd_get_passphrase): Use the cache.
|
||||
(cmd_clear_passphrase): Ditto.
|
||||
|
||||
* gpg-agent.c: Removed unused cruft and implement the socket
|
||||
based server.
|
||||
(my_strusage): Take bug report address from configure.ac.
|
||||
* command.c (start_command_handler): Add an argument to start as
|
||||
regular server.
|
||||
(start_command_handler): Enable Assuan logging.
|
||||
|
||||
2002-01-15 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* trustlist.c: New.
|
||||
* command.c (cmd_istrusted, cmd_listtrusted, cmd_marktrusted): New.
|
||||
|
||||
2002-01-07 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* genkey.c: Store the secret part and return the public part.
|
||||
|
||||
2002-01-03 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* command.c (cmd_get_passphrase): New.
|
||||
(cmd_clear_passphrase): New.
|
||||
* query.c (agent_get_passphrase): New.
|
||||
|
||||
2002-01-02 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* genkey.c: New.
|
||||
* command.c (cmd_genkey): New.
|
||||
|
||||
* command.c (rc_to_assuan_status): Removed and changed all callers
|
||||
to use map_to_assuan_status.
|
||||
|
||||
2001-12-19 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* keyformat.txt: New.
|
||||
|
||||
2001-12-19 Marcus Brinkmann <marcus@g10code.de>
|
||||
|
||||
* query.c (start_pinentry): Add new argument to assuan_pipe_connect.
|
||||
|
||||
2001-12-18 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* Makefile.am: Use LIBGCRYPT macros
|
||||
|
||||
2001-12-14 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpg-agent.c (main): New option --batch. New option --debug-wait
|
||||
n, so that it is possible to attach gdb when used in server mode.
|
||||
* query.c (agent_askpin): Don't ask in batch mode.
|
||||
|
||||
* command.c: Removed the conversion macros as they are now in
|
||||
../common/util.h.
|
||||
|
||||
2001-12-14 Marcus Brinkmann <marcus@g10code.de>
|
||||
|
||||
* query.c (LINELENGTH): Removed.
|
||||
(agent_askpin): Use ASSUAN_LINELENGTH, not LINELENGTH.
|
||||
|
||||
2001-11-19 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpg-agent.c: Removed all GUI code, removed code for old
|
||||
protocol. New code to use the Assuan protocol as a server and
|
||||
also to communicate with a new ask-passphrase utility.
|
||||
|
||||
2000-11-22 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* gpg-agent.c (main): csh support by Dan Winship, new options --sh
|
||||
and --csh and set default by consulting $SHELL.
|
||||
|
||||
Mon Aug 21 17:59:17 CEST 2000 Werner Koch <wk@openit.de>
|
||||
|
||||
* gpg-agent.c (passphrase_dialog): Cleanup the window and added the
|
||||
user supplied text to the window.
|
||||
(main): Fixed segv in gtk_init when used without a command to start.
|
||||
|
||||
* gpg-agent.c: --flush option.
|
||||
(req_flush): New.
|
||||
(req_clear_passphrase): Implemented.
|
||||
|
||||
Fri Aug 18 14:27:14 CEST 2000 Werner Koch <wk@openit.de>
|
||||
|
||||
* gpg-agent.c: New.
|
||||
* Makefile.am: New.
|
||||
|
||||
|
||||
Copyright 2001, 2002 Free Software Foundation, Inc.
|
||||
|
||||
This file is free software; as a special exception the author gives
|
||||
unlimited permission to copy and/or distribute it, with or without
|
||||
modifications, as long as this notice is preserved.
|
||||
|
||||
This file is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
@ -1,62 +0,0 @@
|
|||
# Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
#
|
||||
# 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 2 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, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
|
||||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
localedir = $(datadir)/locale
|
||||
INCLUDES = -I../intl -DLOCALEDIR=\"$(localedir)\"
|
||||
|
||||
bin_PROGRAMS = gpg-agent
|
||||
pkglib_PROGRAMS = gpg-protect-tool
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/common $(LIBGCRYPT_CFLAGS) $(PTH_CFLAGS)
|
||||
LDFLAGS = @LDFLAGS@
|
||||
|
||||
gpg_agent_SOURCES = \
|
||||
gpg-agent.c agent.h \
|
||||
command.c \
|
||||
query.c \
|
||||
cache.c \
|
||||
trans.c \
|
||||
findkey.c \
|
||||
pksign.c \
|
||||
pkdecrypt.c \
|
||||
genkey.c \
|
||||
protect.c \
|
||||
trustlist.c \
|
||||
divert-scd.c \
|
||||
call-scd.c \
|
||||
learncard.c \
|
||||
sexp-parse.h
|
||||
|
||||
|
||||
gpg_agent_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a \
|
||||
../common/libcommon.a $(LIBGCRYPT_LIBS) $(PTH_LIBS)
|
||||
|
||||
gpg_protect_tool_SOURCES = \
|
||||
protect-tool.c \
|
||||
protect.c \
|
||||
minip12.c minip12.h \
|
||||
simple-pwquery.c simple-pwquery.h
|
||||
|
||||
gpg_protect_tool_LDADD = ../jnlib/libjnlib.a \
|
||||
../common/libcommon.a $(LIBGCRYPT_LIBS)
|
||||
|
||||
|
||||
|
||||
|
197
agent/agent.h
197
agent/agent.h
|
@ -1,197 +0,0 @@
|
|||
/* agent.h - Global definitions for the agent
|
||||
* Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#ifndef AGENT_H
|
||||
#define AGENT_H
|
||||
|
||||
#include <gcrypt.h>
|
||||
#include "../common/util.h"
|
||||
#include "../common/errors.h"
|
||||
|
||||
#define MAX_DIGEST_LEN 24
|
||||
|
||||
/* A large struct name "opt" to keep global flags */
|
||||
struct {
|
||||
unsigned int debug; /* debug flags (DBG_foo_VALUE) */
|
||||
int verbose; /* verbosity level */
|
||||
int quiet; /* be as quiet as possible */
|
||||
int dry_run; /* don't change any persistent data */
|
||||
int batch; /* batch mode */
|
||||
const char *homedir; /* configuration directory name */
|
||||
const char *pinentry_program;
|
||||
char *display;
|
||||
char *ttyname;
|
||||
char *ttytype;
|
||||
char *lc_ctype;
|
||||
char *lc_messages;
|
||||
const char *scdaemon_program;
|
||||
int no_grab; /* don't let the pinentry grab the keyboard */
|
||||
unsigned long def_cache_ttl;
|
||||
|
||||
int running_detached; /* we are running detached from the tty. */
|
||||
|
||||
int ignore_cache_for_signing;
|
||||
int keep_tty; /* don't switch the TTY (for pinentry) on request */
|
||||
int keep_display; /* don't switch the DISPLAY (for pinentry) on request */
|
||||
} opt;
|
||||
|
||||
|
||||
#define DBG_COMMAND_VALUE 1 /* debug commands i/o */
|
||||
#define DBG_MPI_VALUE 2 /* debug mpi details */
|
||||
#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */
|
||||
#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */
|
||||
#define DBG_CACHE_VALUE 64 /* debug the caching */
|
||||
#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */
|
||||
#define DBG_HASHING_VALUE 512 /* debug hashing operations */
|
||||
#define DBG_ASSUAN_VALUE 1024
|
||||
|
||||
#define DBG_COMMAND (opt.debug & DBG_COMMAND_VALUE)
|
||||
#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE)
|
||||
#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE)
|
||||
#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE)
|
||||
#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
|
||||
#define DBG_ASSUAN (opt.debug & DBG_ASSUAN_VALUE)
|
||||
|
||||
struct server_local_s;
|
||||
|
||||
struct server_control_s {
|
||||
struct server_local_s *server_local;
|
||||
struct {
|
||||
int algo;
|
||||
unsigned char value[MAX_DIGEST_LEN];
|
||||
int valuelen;
|
||||
} digest;
|
||||
char keygrip[20];
|
||||
int have_keygrip;
|
||||
|
||||
};
|
||||
typedef struct server_control_s *CTRL;
|
||||
|
||||
|
||||
struct pin_entry_info_s {
|
||||
int min_digits; /* min. number of digits required or 0 for freeform entry */
|
||||
int max_digits; /* max. number of allowed digits allowed*/
|
||||
int max_tries;
|
||||
int failed_tries;
|
||||
int (*check_cb)(struct pin_entry_info_s *); /* CB used to check the PIN */
|
||||
void *check_cb_arg; /* optional argument which might be of use in the CB */
|
||||
const char *cb_errtext; /* used by the cb to displaye a specific error */
|
||||
size_t max_length; /* allocated length of the buffer */
|
||||
char pin[1];
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
PRIVATE_KEY_UNKNOWN = 0,
|
||||
PRIVATE_KEY_CLEAR = 1,
|
||||
PRIVATE_KEY_PROTECTED = 2,
|
||||
PRIVATE_KEY_SHADOWED = 3
|
||||
};
|
||||
|
||||
/*-- gpg-agent.c --*/
|
||||
void agent_exit (int rc); /* also implemented in other tools */
|
||||
|
||||
/*-- command.c --*/
|
||||
void start_command_handler (int, int);
|
||||
|
||||
/*-- findkey.c --*/
|
||||
int agent_write_private_key (const unsigned char *grip,
|
||||
const void *buffer, size_t length, int force);
|
||||
GCRY_SEXP agent_key_from_file (const unsigned char *grip,
|
||||
unsigned char **shadow_info, int ignore_cache);
|
||||
int agent_key_available (const unsigned char *grip);
|
||||
|
||||
/*-- query.c --*/
|
||||
int agent_askpin (const char *desc_text, struct pin_entry_info_s *pininfo);
|
||||
int agent_get_passphrase (char **retpass,
|
||||
const char *desc, const char *prompt,
|
||||
const char *errtext);
|
||||
int agent_get_confirmation (const char *desc, const char *ok,
|
||||
const char *cancel);
|
||||
|
||||
/*-- cache.c --*/
|
||||
void agent_flush_cache (void);
|
||||
int agent_put_cache (const char *key, const char *data, int ttl);
|
||||
const char *agent_get_cache (const char *key, void **cache_id);
|
||||
void agent_unlock_cache_entry (void **cache_id);
|
||||
|
||||
|
||||
/*-- pksign.c --*/
|
||||
int agent_pksign (CTRL ctrl, FILE *outfp, int ignore_cache);
|
||||
|
||||
/*-- pkdecrypt.c --*/
|
||||
int agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen,
|
||||
FILE *outfp);
|
||||
|
||||
/*-- genkey.c --*/
|
||||
int agent_genkey (CTRL ctrl,
|
||||
const char *keyparam, size_t keyparmlen, FILE *outfp);
|
||||
|
||||
/*-- protect.c --*/
|
||||
int agent_protect (const unsigned char *plainkey, const char *passphrase,
|
||||
unsigned char **result, size_t *resultlen);
|
||||
int agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
|
||||
unsigned char **result, size_t *resultlen);
|
||||
int agent_private_key_type (const unsigned char *privatekey);
|
||||
int agent_shadow_key (const unsigned char *pubkey,
|
||||
const unsigned char *shadow_info,
|
||||
unsigned char **result);
|
||||
int agent_get_shadow_info (const unsigned char *shadowkey,
|
||||
unsigned char const **shadow_info);
|
||||
|
||||
|
||||
/*-- trustlist.c --*/
|
||||
int agent_istrusted (const char *fpr);
|
||||
int agent_listtrusted (void *assuan_context);
|
||||
int agent_marktrusted (const char *name, const char *fpr, int flag);
|
||||
|
||||
|
||||
/*-- divert-scd.c --*/
|
||||
int divert_pksign (const unsigned char *digest, size_t digestlen, int algo,
|
||||
const unsigned char *shadow_info, unsigned char **r_sig);
|
||||
int divert_pkdecrypt (const unsigned char *cipher,
|
||||
const unsigned char *shadow_info,
|
||||
char **r_buf, size_t *r_len);
|
||||
|
||||
/*-- call-scd.c --*/
|
||||
int agent_card_learn (void (*kpinfo_cb)(void*, const char *),
|
||||
void *kpinfo_cb_arg,
|
||||
void (*certinfo_cb)(void*, const char *),
|
||||
void *certinfo_cb_arg);
|
||||
int agent_card_serialno (char **r_serialno);
|
||||
int agent_card_pksign (const char *keyid,
|
||||
int (*getpin_cb)(void *, const char *, char*, size_t),
|
||||
void *getpin_cb_arg,
|
||||
const unsigned char *indata, size_t indatalen,
|
||||
char **r_buf, size_t *r_buflen);
|
||||
int agent_card_pkdecrypt (const char *keyid,
|
||||
int (*getpin_cb)(void *, const char *, char*,size_t),
|
||||
void *getpin_cb_arg,
|
||||
const unsigned char *indata, size_t indatalen,
|
||||
char **r_buf, size_t *r_buflen);
|
||||
int agent_card_readcert (const char *id, char **r_buf, size_t *r_buflen);
|
||||
int agent_card_readkey (const char *id, unsigned char **r_buf);
|
||||
|
||||
|
||||
/*-- learncard.c --*/
|
||||
int agent_handle_learn (void *assuan_context);
|
||||
|
||||
|
||||
#endif /*AGENT_H*/
|
314
agent/cache.c
314
agent/cache.c
|
@ -1,314 +0,0 @@
|
|||
/* cache.c - keep a cache of passphrases
|
||||
* Copyright (C) 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "agent.h"
|
||||
|
||||
struct secret_data_s {
|
||||
int totallen; /* this includes the padding */
|
||||
int datalen; /* actual data length */
|
||||
char data[1];
|
||||
};
|
||||
|
||||
typedef struct cache_item_s *ITEM;
|
||||
struct cache_item_s {
|
||||
ITEM next;
|
||||
time_t created;
|
||||
time_t accessed;
|
||||
int ttl; /* max. lifetime given in seonds */
|
||||
int lockcount;
|
||||
struct secret_data_s *pw;
|
||||
char key[1];
|
||||
};
|
||||
|
||||
|
||||
static ITEM thecache;
|
||||
|
||||
|
||||
static void
|
||||
release_data (struct secret_data_s *data)
|
||||
{
|
||||
xfree (data);
|
||||
}
|
||||
|
||||
static struct secret_data_s *
|
||||
new_data (const void *data, size_t length)
|
||||
{
|
||||
struct secret_data_s *d;
|
||||
int total;
|
||||
|
||||
/* we pad the data to 32 bytes so that it get more complicated
|
||||
finding something out by watching allocation patterns. This is
|
||||
usally not possible but we better assume nothing about our
|
||||
secure storage provider*/
|
||||
total = length + 32 - (length % 32);
|
||||
|
||||
d = gcry_malloc_secure (sizeof d + total - 1);
|
||||
if (d)
|
||||
{
|
||||
d->totallen = total;
|
||||
d->datalen = length;
|
||||
memcpy (d->data, data, length);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
/* check whether there are items to expire */
|
||||
static void
|
||||
housekeeping (void)
|
||||
{
|
||||
ITEM r, rprev;
|
||||
time_t current = gnupg_get_time ();
|
||||
|
||||
/* first expire the actual data */
|
||||
for (r=thecache; r; r = r->next)
|
||||
{
|
||||
if (!r->lockcount && r->pw && r->accessed + r->ttl < current)
|
||||
{
|
||||
if (DBG_CACHE)
|
||||
log_debug (" expired `%s' (%ds after last access)\n",
|
||||
r->key, r->ttl);
|
||||
release_data (r->pw);
|
||||
r->pw = NULL;
|
||||
r->accessed = current;
|
||||
}
|
||||
}
|
||||
|
||||
/* second, make sure that we also remove them based on the created stamp so
|
||||
that the user has to enter it from time to time. We do this every hour */
|
||||
for (r=thecache; r; r = r->next)
|
||||
{
|
||||
if (!r->lockcount && r->pw && r->created + 60*60 < current)
|
||||
{
|
||||
if (DBG_CACHE)
|
||||
log_debug (" expired `%s' (1h after creation)\n", r->key);
|
||||
release_data (r->pw);
|
||||
r->pw = NULL;
|
||||
r->accessed = current;
|
||||
}
|
||||
}
|
||||
|
||||
/* third, make sure that we don't have too many items in the list.
|
||||
Expire old and unused entries after 30 minutes */
|
||||
for (rprev=NULL, r=thecache; r; )
|
||||
{
|
||||
if (!r->pw && r->accessed + 60*30 < current)
|
||||
{
|
||||
if (r->lockcount)
|
||||
{
|
||||
log_error ("can't remove unused cache entry `%s' due to"
|
||||
" lockcount=%d\n",
|
||||
r->key, r->lockcount);
|
||||
r->accessed += 60*10; /* next error message in 10 minutes */
|
||||
rprev = r;
|
||||
r = r->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
ITEM r2 = r->next;
|
||||
if (DBG_CACHE)
|
||||
log_debug (" removed `%s' (slot not used for 30m)\n", r->key);
|
||||
xfree (r);
|
||||
if (!rprev)
|
||||
thecache = r2;
|
||||
else
|
||||
rprev->next = r2;
|
||||
r = r2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rprev = r;
|
||||
r = r->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
agent_flush_cache (void)
|
||||
{
|
||||
ITEM r;
|
||||
|
||||
if (DBG_CACHE)
|
||||
log_debug ("agent_flush_cache\n");
|
||||
|
||||
for (r=thecache; r; r = r->next)
|
||||
{
|
||||
if (!r->lockcount && r->pw)
|
||||
{
|
||||
if (DBG_CACHE)
|
||||
log_debug (" flushing `%s'\n", r->key);
|
||||
release_data (r->pw);
|
||||
r->pw = NULL;
|
||||
r->accessed = 0;
|
||||
}
|
||||
else if (r->lockcount && r->pw)
|
||||
{
|
||||
if (DBG_CACHE)
|
||||
log_debug (" marked `%s' for flushing\n", r->key);
|
||||
r->accessed = 0;
|
||||
r->ttl = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Store DATA of length DATALEN in the cache under KEY and mark it
|
||||
with a maximum lifetime of TTL seconds. If there is already data
|
||||
under this key, it will be replaced. Using a DATA of NULL deletes
|
||||
the entry */
|
||||
int
|
||||
agent_put_cache (const char *key, const char *data, int ttl)
|
||||
{
|
||||
ITEM r;
|
||||
|
||||
if (DBG_CACHE)
|
||||
log_debug ("agent_put_cache `%s'\n", key);
|
||||
housekeeping ();
|
||||
|
||||
if (ttl < 1)
|
||||
ttl = opt.def_cache_ttl;
|
||||
if (!ttl)
|
||||
return 0;
|
||||
|
||||
for (r=thecache; r; r = r->next)
|
||||
{
|
||||
if (!r->lockcount && !strcmp (r->key, key))
|
||||
break;
|
||||
}
|
||||
if (r)
|
||||
{ /* replace */
|
||||
if (r->pw)
|
||||
{
|
||||
release_data (r->pw);
|
||||
r->pw = NULL;
|
||||
}
|
||||
if (data)
|
||||
{
|
||||
r->created = r->accessed = gnupg_get_time ();
|
||||
r->ttl = ttl;
|
||||
r->pw = new_data (data, strlen (data)+1);
|
||||
if (!r->pw)
|
||||
log_error ("out of core while allocating new cache item\n");
|
||||
}
|
||||
}
|
||||
else if (data)
|
||||
{ /* simply insert */
|
||||
r = xtrycalloc (1, sizeof *r + strlen (key));
|
||||
if (!r)
|
||||
log_error ("out of core while allocating new cache control\n");
|
||||
else
|
||||
{
|
||||
strcpy (r->key, key);
|
||||
r->created = r->accessed = gnupg_get_time ();
|
||||
r->ttl = ttl;
|
||||
r->pw = new_data (data, strlen (data)+1);
|
||||
if (!r->pw)
|
||||
{
|
||||
log_error ("out of core while allocating new cache item\n");
|
||||
xfree (r);
|
||||
}
|
||||
else
|
||||
{
|
||||
r->next = thecache;
|
||||
thecache = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Try to find an item in the cache */
|
||||
const char *
|
||||
agent_get_cache (const char *key, void **cache_id)
|
||||
{
|
||||
ITEM r;
|
||||
|
||||
if (DBG_CACHE)
|
||||
log_debug ("agent_get_cache `%s'...\n", key);
|
||||
housekeeping ();
|
||||
|
||||
/* first try to find one with no locks - this is an updated cache
|
||||
entry: We might have entries with a lockcount and without a
|
||||
lockcount. */
|
||||
for (r=thecache; r; r = r->next)
|
||||
{
|
||||
if (!r->lockcount && r->pw && !strcmp (r->key, key))
|
||||
{
|
||||
/* put_cache does only put strings into the cache, so we
|
||||
don't need the lengths */
|
||||
r->accessed = gnupg_get_time ();
|
||||
if (DBG_CACHE)
|
||||
log_debug ("... hit\n");
|
||||
r->lockcount++;
|
||||
*cache_id = r;
|
||||
return r->pw->data;
|
||||
}
|
||||
}
|
||||
/* again, but this time get even one with a lockcount set */
|
||||
for (r=thecache; r; r = r->next)
|
||||
{
|
||||
if (r->pw && !strcmp (r->key, key))
|
||||
{
|
||||
r->accessed = gnupg_get_time ();
|
||||
if (DBG_CACHE)
|
||||
log_debug ("... hit (locked)\n");
|
||||
r->lockcount++;
|
||||
*cache_id = r;
|
||||
return r->pw->data;
|
||||
}
|
||||
}
|
||||
if (DBG_CACHE)
|
||||
log_debug ("... miss\n");
|
||||
|
||||
*cache_id = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
agent_unlock_cache_entry (void **cache_id)
|
||||
{
|
||||
ITEM r;
|
||||
|
||||
for (r=thecache; r; r = r->next)
|
||||
{
|
||||
if (r == *cache_id)
|
||||
{
|
||||
if (!r->lockcount)
|
||||
log_error ("trying to unlock non-locked cache entry `%s'\n",
|
||||
r->key);
|
||||
else
|
||||
r->lockcount--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
592
agent/call-scd.c
592
agent/call-scd.c
|
@ -1,592 +0,0 @@
|
|||
/* call-scd.c - fork of the scdaemon to do SC operations
|
||||
* Copyright (C) 2001, 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
/* Fixme: For now we have serialized all access to the scdaemon which
|
||||
make sense becuase the scdaemon can't handle concurrent connections
|
||||
right now. We should however keep a list of connections and lock
|
||||
just that connection - it migth make sense to implemtn parts of
|
||||
this in Assuan.*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#ifdef USE_GNU_PTH
|
||||
# include <pth.h>
|
||||
#endif
|
||||
|
||||
#include "agent.h"
|
||||
#include "../assuan/assuan.h"
|
||||
|
||||
#ifdef _POSIX_OPEN_MAX
|
||||
#define MAX_OPEN_FDS _POSIX_OPEN_MAX
|
||||
#else
|
||||
#define MAX_OPEN_FDS 20
|
||||
#endif
|
||||
|
||||
static ASSUAN_CONTEXT scd_ctx = NULL;
|
||||
#ifdef USE_GNU_PTH
|
||||
static pth_mutex_t scd_lock = PTH_MUTEX_INIT;
|
||||
#endif
|
||||
|
||||
/* callback parameter for learn card */
|
||||
struct learn_parm_s {
|
||||
void (*kpinfo_cb)(void*, const char *);
|
||||
void *kpinfo_cb_arg;
|
||||
void (*certinfo_cb)(void*, const char *);
|
||||
void *certinfo_cb_arg;
|
||||
};
|
||||
|
||||
struct inq_needpin_s {
|
||||
ASSUAN_CONTEXT ctx;
|
||||
int (*getpin_cb)(void *, const char *, char*, size_t);
|
||||
void *getpin_cb_arg;
|
||||
};
|
||||
|
||||
struct membuf {
|
||||
size_t len;
|
||||
size_t size;
|
||||
char *buf;
|
||||
int out_of_core;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* A simple implementation of a dynamic buffer. Use init_membuf() to
|
||||
create a buffer, put_membuf to append bytes and get_membuf to
|
||||
release and return the buffer. Allocation errors are detected but
|
||||
only returned at the final get_membuf(), this helps not to clutter
|
||||
the code with out of core checks. */
|
||||
|
||||
static void
|
||||
init_membuf (struct membuf *mb, int initiallen)
|
||||
{
|
||||
mb->len = 0;
|
||||
mb->size = initiallen;
|
||||
mb->out_of_core = 0;
|
||||
mb->buf = xtrymalloc (initiallen);
|
||||
if (!mb->buf)
|
||||
mb->out_of_core = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
put_membuf (struct membuf *mb, const void *buf, size_t len)
|
||||
{
|
||||
if (mb->out_of_core)
|
||||
return;
|
||||
|
||||
if (mb->len + len >= mb->size)
|
||||
{
|
||||
char *p;
|
||||
|
||||
mb->size += len + 1024;
|
||||
p = xtryrealloc (mb->buf, mb->size);
|
||||
if (!p)
|
||||
{
|
||||
mb->out_of_core = 1;
|
||||
return;
|
||||
}
|
||||
mb->buf = p;
|
||||
}
|
||||
memcpy (mb->buf + mb->len, buf, len);
|
||||
mb->len += len;
|
||||
}
|
||||
|
||||
static void *
|
||||
get_membuf (struct membuf *mb, size_t *len)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (mb->out_of_core)
|
||||
{
|
||||
xfree (mb->buf);
|
||||
mb->buf = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = mb->buf;
|
||||
*len = mb->len;
|
||||
mb->buf = NULL;
|
||||
mb->out_of_core = 1; /* don't allow a reuse */
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static int
|
||||
unlock_scd (int rc)
|
||||
{
|
||||
#ifdef USE_GNU_PTH
|
||||
if (!pth_mutex_release (&scd_lock))
|
||||
{
|
||||
log_error ("failed to release the SCD lock\n");
|
||||
if (!rc)
|
||||
rc = GNUPG_Internal_Error;
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Fork off the SCdaemon if this has not already been done */
|
||||
static int
|
||||
start_scd (void)
|
||||
{
|
||||
int rc;
|
||||
const char *pgmname;
|
||||
ASSUAN_CONTEXT ctx;
|
||||
const char *argv[3];
|
||||
int no_close_list[3];
|
||||
int i;
|
||||
|
||||
#ifdef USE_GNU_PTH
|
||||
if (!pth_mutex_acquire (&scd_lock, 0, NULL))
|
||||
{
|
||||
log_error ("failed to acquire the SCD lock\n");
|
||||
return GNUPG_Internal_Error;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (scd_ctx)
|
||||
return 0; /* No need to serialize things because the agent is
|
||||
expected to tun as a single-thread (or may be in
|
||||
future using libpth) */
|
||||
|
||||
if (opt.verbose)
|
||||
log_info ("no running SCdaemon - starting it\n");
|
||||
|
||||
if (fflush (NULL))
|
||||
{
|
||||
log_error ("error flushing pending output: %s\n", strerror (errno));
|
||||
return unlock_scd (seterr (Write_Error));
|
||||
}
|
||||
|
||||
if (!opt.scdaemon_program || !*opt.scdaemon_program)
|
||||
opt.scdaemon_program = GNUPG_DEFAULT_SCDAEMON;
|
||||
if ( !(pgmname = strrchr (opt.scdaemon_program, '/')))
|
||||
pgmname = opt.scdaemon_program;
|
||||
else
|
||||
pgmname++;
|
||||
|
||||
argv[0] = pgmname;
|
||||
argv[1] = "--server";
|
||||
argv[2] = NULL;
|
||||
|
||||
i=0;
|
||||
if (!opt.running_detached)
|
||||
{
|
||||
if (log_get_fd () != -1)
|
||||
no_close_list[i++] = log_get_fd ();
|
||||
no_close_list[i++] = fileno (stderr);
|
||||
}
|
||||
no_close_list[i] = -1;
|
||||
|
||||
/* connect to the pinentry and perform initial handshaking */
|
||||
rc = assuan_pipe_connect (&ctx, opt.scdaemon_program, (char**)argv,
|
||||
no_close_list);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("can't connect to the SCdaemon: %s\n",
|
||||
assuan_strerror (rc));
|
||||
return unlock_scd (seterr (No_Scdaemon));
|
||||
}
|
||||
scd_ctx = ctx;
|
||||
|
||||
if (DBG_ASSUAN)
|
||||
log_debug ("connection to SCdaemon established\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static AssuanError
|
||||
learn_status_cb (void *opaque, const char *line)
|
||||
{
|
||||
struct learn_parm_s *parm = opaque;
|
||||
const char *keyword = line;
|
||||
int keywordlen;
|
||||
|
||||
for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
|
||||
;
|
||||
while (spacep (line))
|
||||
line++;
|
||||
if (keywordlen == 8 && !memcmp (keyword, "CERTINFO", keywordlen))
|
||||
{
|
||||
parm->certinfo_cb (parm->certinfo_cb_arg, line);
|
||||
}
|
||||
else if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen))
|
||||
{
|
||||
parm->kpinfo_cb (parm->kpinfo_cb_arg, line);
|
||||
}
|
||||
else if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen))
|
||||
{
|
||||
log_debug ("learn_status_cb: serialno `%s'\n", line);
|
||||
}
|
||||
else
|
||||
log_debug ("learn_status_cb: ignoring `%.*s'\n", keywordlen, keyword);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Perform the learn command and return a list of all private keys
|
||||
stored on the card. */
|
||||
int
|
||||
agent_card_learn (void (*kpinfo_cb)(void*, const char *),
|
||||
void *kpinfo_cb_arg,
|
||||
void (*certinfo_cb)(void*, const char *),
|
||||
void *certinfo_cb_arg)
|
||||
{
|
||||
int rc;
|
||||
struct learn_parm_s parm;
|
||||
|
||||
rc = start_scd ();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
memset (&parm, 0, sizeof parm);
|
||||
parm.kpinfo_cb = kpinfo_cb;
|
||||
parm.kpinfo_cb_arg = kpinfo_cb_arg;
|
||||
parm.certinfo_cb = certinfo_cb;
|
||||
parm.certinfo_cb_arg = certinfo_cb_arg;
|
||||
rc = assuan_transact (scd_ctx, "LEARN --force",
|
||||
NULL, NULL, NULL, NULL,
|
||||
learn_status_cb, &parm);
|
||||
if (rc)
|
||||
return unlock_scd (map_assuan_err (rc));
|
||||
|
||||
return unlock_scd (0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static AssuanError
|
||||
get_serialno_cb (void *opaque, const char *line)
|
||||
{
|
||||
char **serialno = opaque;
|
||||
const char *keyword = line;
|
||||
const char *s;
|
||||
int keywordlen, n;
|
||||
|
||||
for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
|
||||
;
|
||||
while (spacep (line))
|
||||
line++;
|
||||
|
||||
if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen))
|
||||
{
|
||||
if (*serialno)
|
||||
return ASSUAN_Unexpected_Status;
|
||||
for (n=0,s=line; hexdigitp (s); s++, n++)
|
||||
;
|
||||
if (!n || (n&1)|| !(spacep (s) || !*s) )
|
||||
return ASSUAN_Invalid_Status;
|
||||
*serialno = xtrymalloc (n+1);
|
||||
if (!*serialno)
|
||||
return ASSUAN_Out_Of_Core;
|
||||
memcpy (*serialno, line, n);
|
||||
(*serialno)[n] = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return the serial number of the card or an appropriate error. The
|
||||
serial number is returned as a hexstring. */
|
||||
int
|
||||
agent_card_serialno (char **r_serialno)
|
||||
{
|
||||
int rc;
|
||||
char *serialno = NULL;
|
||||
|
||||
rc = start_scd ();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Hmm, do we really need this reset - scddaemon should do this or
|
||||
we can do this if we for some reason figure out that the
|
||||
operation might have failed due to a missing RESET. Hmmm, I feel
|
||||
this is really SCdaemon's duty */
|
||||
rc = assuan_transact (scd_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return unlock_scd (map_assuan_err (rc));
|
||||
|
||||
rc = assuan_transact (scd_ctx, "SERIALNO",
|
||||
NULL, NULL, NULL, NULL,
|
||||
get_serialno_cb, &serialno);
|
||||
if (rc)
|
||||
{
|
||||
xfree (serialno);
|
||||
return unlock_scd (map_assuan_err (rc));
|
||||
}
|
||||
*r_serialno = serialno;
|
||||
return unlock_scd (0);
|
||||
}
|
||||
|
||||
|
||||
static AssuanError
|
||||
membuf_data_cb (void *opaque, const void *buffer, size_t length)
|
||||
{
|
||||
struct membuf *data = opaque;
|
||||
|
||||
if (buffer)
|
||||
put_membuf (data, buffer, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handle the NEEDPIN inquiry. */
|
||||
static AssuanError
|
||||
inq_needpin (void *opaque, const char *line)
|
||||
{
|
||||
struct inq_needpin_s *parm = opaque;
|
||||
char *pin;
|
||||
size_t pinlen;
|
||||
int rc;
|
||||
|
||||
if (!(!strncmp (line, "NEEDPIN", 7) && (line[7] == ' ' || !line[7])))
|
||||
{
|
||||
log_error ("unsupported inquiry `%s'\n", line);
|
||||
return ASSUAN_Inquire_Unknown;
|
||||
}
|
||||
line += 7;
|
||||
|
||||
pinlen = 90;
|
||||
pin = gcry_malloc_secure (pinlen);
|
||||
if (!pin)
|
||||
return ASSUAN_Out_Of_Core;
|
||||
|
||||
rc = parm->getpin_cb (parm->getpin_cb_arg, line, pin, pinlen);
|
||||
if (rc)
|
||||
rc = ASSUAN_Canceled;
|
||||
if (!rc)
|
||||
rc = assuan_send_data (parm->ctx, pin, pinlen);
|
||||
xfree (pin);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Create a signature using the current card */
|
||||
int
|
||||
agent_card_pksign (const char *keyid,
|
||||
int (*getpin_cb)(void *, const char *, char*, size_t),
|
||||
void *getpin_cb_arg,
|
||||
const unsigned char *indata, size_t indatalen,
|
||||
char **r_buf, size_t *r_buflen)
|
||||
{
|
||||
int rc, i;
|
||||
char *p, line[ASSUAN_LINELENGTH];
|
||||
struct membuf data;
|
||||
struct inq_needpin_s inqparm;
|
||||
size_t len;
|
||||
unsigned char *sigbuf;
|
||||
size_t sigbuflen;
|
||||
|
||||
*r_buf = NULL;
|
||||
rc = start_scd ();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (indatalen*2 + 50 > DIM(line))
|
||||
return unlock_scd (seterr (General_Error));
|
||||
|
||||
sprintf (line, "SETDATA ");
|
||||
p = line + strlen (line);
|
||||
for (i=0; i < indatalen ; i++, p += 2 )
|
||||
sprintf (p, "%02X", indata[i]);
|
||||
rc = assuan_transact (scd_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return unlock_scd (map_assuan_err (rc));
|
||||
|
||||
init_membuf (&data, 1024);
|
||||
inqparm.ctx = scd_ctx;
|
||||
inqparm.getpin_cb = getpin_cb;
|
||||
inqparm.getpin_cb_arg = getpin_cb_arg;
|
||||
snprintf (line, DIM(line)-1, "PKSIGN %s", keyid);
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (scd_ctx, line,
|
||||
membuf_data_cb, &data,
|
||||
inq_needpin, &inqparm,
|
||||
NULL, NULL);
|
||||
if (rc)
|
||||
{
|
||||
xfree (get_membuf (&data, &len));
|
||||
return unlock_scd (map_assuan_err (rc));
|
||||
}
|
||||
sigbuf = get_membuf (&data, &sigbuflen);
|
||||
|
||||
/* create an S-expression from it which is formatted like this:
|
||||
"(7:sig-val(3:rsa(1:sSIGBUFLEN:SIGBUF)))" */
|
||||
*r_buflen = 21 + 11 + sigbuflen + 4;
|
||||
*r_buf = xtrymalloc (*r_buflen);
|
||||
if (!*r_buf)
|
||||
{
|
||||
xfree (*r_buf);
|
||||
return unlock_scd (GNUPG_Out_Of_Core);
|
||||
}
|
||||
p = stpcpy (*r_buf, "(7:sig-val(3:rsa(1:s" );
|
||||
sprintf (p, "%u:", (unsigned int)sigbuflen);
|
||||
p += strlen (p);
|
||||
memcpy (p, sigbuf, sigbuflen);
|
||||
p += sigbuflen;
|
||||
strcpy (p, ")))");
|
||||
xfree (sigbuf);
|
||||
|
||||
assert (gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL));
|
||||
return unlock_scd (0);
|
||||
}
|
||||
|
||||
/* Decipher INDATA using the current card. Note that the returned value is */
|
||||
int
|
||||
agent_card_pkdecrypt (const char *keyid,
|
||||
int (*getpin_cb)(void *, const char *, char*, size_t),
|
||||
void *getpin_cb_arg,
|
||||
const unsigned char *indata, size_t indatalen,
|
||||
char **r_buf, size_t *r_buflen)
|
||||
{
|
||||
int rc, i;
|
||||
char *p, line[ASSUAN_LINELENGTH];
|
||||
struct membuf data;
|
||||
struct inq_needpin_s inqparm;
|
||||
size_t len;
|
||||
|
||||
*r_buf = NULL;
|
||||
rc = start_scd ();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* FIXME: use secure memory where appropriate */
|
||||
if (indatalen*2 + 50 > DIM(line))
|
||||
return unlock_scd (seterr (General_Error));
|
||||
|
||||
sprintf (line, "SETDATA ");
|
||||
p = line + strlen (line);
|
||||
for (i=0; i < indatalen ; i++, p += 2 )
|
||||
sprintf (p, "%02X", indata[i]);
|
||||
rc = assuan_transact (scd_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return unlock_scd (map_assuan_err (rc));
|
||||
|
||||
init_membuf (&data, 1024);
|
||||
inqparm.ctx = scd_ctx;
|
||||
inqparm.getpin_cb = getpin_cb;
|
||||
inqparm.getpin_cb_arg = getpin_cb_arg;
|
||||
snprintf (line, DIM(line)-1, "PKDECRYPT %s", keyid);
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (scd_ctx, line,
|
||||
membuf_data_cb, &data,
|
||||
inq_needpin, &inqparm,
|
||||
NULL, NULL);
|
||||
if (rc)
|
||||
{
|
||||
xfree (get_membuf (&data, &len));
|
||||
return unlock_scd (map_assuan_err (rc));
|
||||
}
|
||||
*r_buf = get_membuf (&data, r_buflen);
|
||||
if (!*r_buf)
|
||||
return unlock_scd (GNUPG_Out_Of_Core);
|
||||
|
||||
return unlock_scd (0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Read a certificate with ID into R_BUF and R_BUFLEN. */
|
||||
int
|
||||
agent_card_readcert (const char *id, char **r_buf, size_t *r_buflen)
|
||||
{
|
||||
int rc;
|
||||
char line[ASSUAN_LINELENGTH];
|
||||
struct membuf data;
|
||||
size_t len;
|
||||
|
||||
*r_buf = NULL;
|
||||
rc = start_scd ();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
init_membuf (&data, 1024);
|
||||
snprintf (line, DIM(line)-1, "READCERT %s", id);
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (scd_ctx, line,
|
||||
membuf_data_cb, &data,
|
||||
NULL, NULL,
|
||||
NULL, NULL);
|
||||
if (rc)
|
||||
{
|
||||
xfree (get_membuf (&data, &len));
|
||||
return unlock_scd (map_assuan_err (rc));
|
||||
}
|
||||
*r_buf = get_membuf (&data, r_buflen);
|
||||
if (!*r_buf)
|
||||
return unlock_scd (GNUPG_Out_Of_Core);
|
||||
|
||||
return unlock_scd (0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Read a key with ID and return it in an allocate buffer pointed to
|
||||
by r_BUF as a valid S-expression. */
|
||||
int
|
||||
agent_card_readkey (const char *id, unsigned char **r_buf)
|
||||
{
|
||||
int rc;
|
||||
char line[ASSUAN_LINELENGTH];
|
||||
struct membuf data;
|
||||
size_t len, buflen;
|
||||
|
||||
*r_buf = NULL;
|
||||
rc = start_scd ();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
init_membuf (&data, 1024);
|
||||
snprintf (line, DIM(line)-1, "READKEY %s", id);
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (scd_ctx, line,
|
||||
membuf_data_cb, &data,
|
||||
NULL, NULL,
|
||||
NULL, NULL);
|
||||
if (rc)
|
||||
{
|
||||
xfree (get_membuf (&data, &len));
|
||||
return unlock_scd (map_assuan_err (rc));
|
||||
}
|
||||
*r_buf = get_membuf (&data, &buflen);
|
||||
if (!*r_buf)
|
||||
return unlock_scd (GNUPG_Out_Of_Core);
|
||||
|
||||
if (!gcry_sexp_canon_len (*r_buf, buflen, NULL, NULL))
|
||||
{
|
||||
xfree (*r_buf); *r_buf = NULL;
|
||||
return unlock_scd (GNUPG_Invalid_Value);
|
||||
}
|
||||
|
||||
return unlock_scd (0);
|
||||
}
|
||||
|
||||
|
||||
|
697
agent/command.c
697
agent/command.c
|
@ -1,697 +0,0 @@
|
|||
/* command.c - gpg-agent command handler
|
||||
* Copyright (C) 2001, 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
/* FIXME: we should not use the default assuan buffering but setup
|
||||
some buffering in secure mempory to protect session keys etc. */
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "agent.h"
|
||||
#include "../assuan/assuan.h"
|
||||
|
||||
/* maximum allowed size of the inquired ciphertext */
|
||||
#define MAXLEN_CIPHERTEXT 4096
|
||||
/* maximum allowed size of the key parameters */
|
||||
#define MAXLEN_KEYPARAM 1024
|
||||
|
||||
#define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t))
|
||||
|
||||
|
||||
#if MAX_DIGEST_LEN < 20
|
||||
#error MAX_DIGEST_LEN shorter than keygrip
|
||||
#endif
|
||||
|
||||
/* Data used to associate an Assuan context with local server data */
|
||||
struct server_local_s {
|
||||
ASSUAN_CONTEXT assuan_ctx;
|
||||
int message_fd;
|
||||
int use_cache_for_signing;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
reset_notify (ASSUAN_CONTEXT ctx)
|
||||
{
|
||||
CTRL ctrl = assuan_get_pointer (ctx);
|
||||
|
||||
memset (ctrl->keygrip, 0, 20);
|
||||
ctrl->have_keygrip = 0;
|
||||
ctrl->digest.valuelen = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Check whether the option NAME appears in LINE */
|
||||
static int
|
||||
has_option (const char *line, const char *name)
|
||||
{
|
||||
const char *s;
|
||||
int n = strlen (name);
|
||||
|
||||
s = strstr (line, name);
|
||||
return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* ISTRUSTED <hexstring_with_fingerprint>
|
||||
|
||||
Return OK when we have an entry with this fingerprint in our
|
||||
trustlist */
|
||||
static int
|
||||
cmd_istrusted (ASSUAN_CONTEXT ctx, char *line)
|
||||
{
|
||||
int rc, n, i;
|
||||
char *p;
|
||||
char fpr[41];
|
||||
|
||||
/* parse the fingerprint value */
|
||||
for (p=line,n=0; hexdigitp (p); p++, n++)
|
||||
;
|
||||
if (*p || !(n == 40 || n == 32))
|
||||
return set_error (Parameter_Error, "invalid fingerprint");
|
||||
i = 0;
|
||||
if (n==32)
|
||||
{
|
||||
strcpy (fpr, "00000000");
|
||||
i += 8;
|
||||
}
|
||||
for (p=line; i < 40; p++, i++)
|
||||
fpr[i] = *p >= 'a'? (*p & 0xdf): *p;
|
||||
fpr[i] = 0;
|
||||
rc = agent_istrusted (fpr);
|
||||
if (!rc)
|
||||
return 0;
|
||||
else if (rc == -1)
|
||||
return ASSUAN_Not_Trusted;
|
||||
else
|
||||
{
|
||||
log_error ("command is_trusted failed: %s\n", gnupg_strerror (rc));
|
||||
return map_to_assuan_status (rc);
|
||||
}
|
||||
}
|
||||
|
||||
/* LISTTRUSTED
|
||||
|
||||
List all entries from the trustlist */
|
||||
static int
|
||||
cmd_listtrusted (ASSUAN_CONTEXT ctx, char *line)
|
||||
{
|
||||
int rc = agent_listtrusted (ctx);
|
||||
if (rc)
|
||||
log_error ("command listtrusted failed: %s\n", gnupg_strerror (rc));
|
||||
return map_to_assuan_status (rc);
|
||||
}
|
||||
|
||||
|
||||
/* MARKTRUSTED <hexstring_with_fingerprint> <flag> <display_name>
|
||||
|
||||
Store a new key in into the trustlist*/
|
||||
static int
|
||||
cmd_marktrusted (ASSUAN_CONTEXT ctx, char *line)
|
||||
{
|
||||
int rc, n, i;
|
||||
char *p;
|
||||
char fpr[41];
|
||||
int flag;
|
||||
|
||||
/* parse the fingerprint value */
|
||||
for (p=line,n=0; hexdigitp (p); p++, n++)
|
||||
;
|
||||
if (!spacep (p) || !(n == 40 || n == 32))
|
||||
return set_error (Parameter_Error, "invalid fingerprint");
|
||||
i = 0;
|
||||
if (n==32)
|
||||
{
|
||||
strcpy (fpr, "00000000");
|
||||
i += 8;
|
||||
}
|
||||
for (p=line; i < 40; p++, i++)
|
||||
fpr[i] = *p >= 'a'? (*p & 0xdf): *p;
|
||||
fpr[i] = 0;
|
||||
|
||||
while (spacep (p))
|
||||
p++;
|
||||
flag = *p++;
|
||||
if ( (flag != 'S' && flag != 'P') || !spacep (p) )
|
||||
return set_error (Parameter_Error, "invalid flag - must be P or S");
|
||||
while (spacep (p))
|
||||
p++;
|
||||
|
||||
rc = agent_marktrusted (p, fpr, flag);
|
||||
if (rc)
|
||||
log_error ("command marktrusted failed: %s\n", gnupg_strerror (rc));
|
||||
return map_to_assuan_status (rc);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* HAVEKEY <hexstring_with_keygrip>
|
||||
|
||||
Return success when the secret key is available */
|
||||
static int
|
||||
cmd_havekey (ASSUAN_CONTEXT ctx, char *line)
|
||||
{
|
||||
int n;
|
||||
char *p;
|
||||
unsigned char buf[20];
|
||||
|
||||
/* parse the hash value */
|
||||
for (p=line,n=0; hexdigitp (p); p++, n++)
|
||||
;
|
||||
if (*p)
|
||||
return set_error (Parameter_Error, "invalid hexstring");
|
||||
if ((n&1))
|
||||
return set_error (Parameter_Error, "odd number of digits");
|
||||
n /= 2;
|
||||
if (n != 20)
|
||||
return set_error (Parameter_Error, "invalid length of keygrip");
|
||||
|
||||
for (p=line, n=0; n < 20; p += 2, n++)
|
||||
buf[n] = xtoi_2 (p);
|
||||
|
||||
if (agent_key_available (buf))
|
||||
return ASSUAN_No_Secret_Key;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* SIGKEY <hexstring_with_keygrip>
|
||||
SETKEY <hexstring_with_keygrip>
|
||||
|
||||
Set the key used for a sign or decrypt operation */
|
||||
static int
|
||||
cmd_sigkey (ASSUAN_CONTEXT ctx, char *line)
|
||||
{
|
||||
int n;
|
||||
char *p;
|
||||
CTRL ctrl = assuan_get_pointer (ctx);
|
||||
unsigned char *buf;
|
||||
|
||||
/* parse the hash value */
|
||||
for (p=line,n=0; hexdigitp (p); p++, n++)
|
||||
;
|
||||
if (*p)
|
||||
return set_error (Parameter_Error, "invalid hexstring");
|
||||
if ((n&1))
|
||||
return set_error (Parameter_Error, "odd number of digits");
|
||||
n /= 2;
|
||||
if (n != 20)
|
||||
return set_error (Parameter_Error, "invalid length of keygrip");
|
||||
|
||||
buf = ctrl->keygrip;
|
||||
for (p=line, n=0; n < 20; p += 2, n++)
|
||||
buf[n] = xtoi_2 (p);
|
||||
ctrl->have_keygrip = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* SETHASH <algonumber> <hexstring>
|
||||
|
||||
The client can use this command to tell the server about the data
|
||||
(which usually is a hash) to be signed. */
|
||||
static int
|
||||
cmd_sethash (ASSUAN_CONTEXT ctx, char *line)
|
||||
{
|
||||
int n;
|
||||
char *p;
|
||||
CTRL ctrl = assuan_get_pointer (ctx);
|
||||
unsigned char *buf;
|
||||
char *endp;
|
||||
int algo;
|
||||
|
||||
/* parse the algo number and check it */
|
||||
algo = (int)strtoul (line, &endp, 10);
|
||||
for (line = endp; *line == ' ' || *line == '\t'; line++)
|
||||
;
|
||||
if (!algo || gcry_md_test_algo (algo))
|
||||
return set_error (Unsupported_Algorithm, NULL);
|
||||
ctrl->digest.algo = algo;
|
||||
|
||||
/* parse the hash value */
|
||||
for (p=line,n=0; hexdigitp (p); p++, n++)
|
||||
;
|
||||
if (*p)
|
||||
return set_error (Parameter_Error, "invalid hexstring");
|
||||
if ((n&1))
|
||||
return set_error (Parameter_Error, "odd number of digits");
|
||||
n /= 2;
|
||||
if (n != 16 && n != 20 && n != 24 && n != 32)
|
||||
return set_error (Parameter_Error, "unsupported length of hash");
|
||||
if (n > MAX_DIGEST_LEN)
|
||||
return set_error (Parameter_Error, "hash value to long");
|
||||
|
||||
buf = ctrl->digest.value;
|
||||
ctrl->digest.valuelen = n;
|
||||
for (p=line, n=0; n < ctrl->digest.valuelen; p += 2, n++)
|
||||
buf[n] = xtoi_2 (p);
|
||||
for (; n < ctrl->digest.valuelen; n++)
|
||||
buf[n] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* PKSIGN <options>
|
||||
|
||||
Perform the actual sign operation. Neither input nor output are
|
||||
sensitive to eavesdropping */
|
||||
static int
|
||||
cmd_pksign (ASSUAN_CONTEXT ctx, char *line)
|
||||
{
|
||||
int rc;
|
||||
int ignore_cache = 0;
|
||||
CTRL ctrl = assuan_get_pointer (ctx);
|
||||
|
||||
if (opt.ignore_cache_for_signing)
|
||||
ignore_cache = 1;
|
||||
else if (!ctrl->server_local->use_cache_for_signing)
|
||||
ignore_cache = 1;
|
||||
|
||||
rc = agent_pksign (ctrl, assuan_get_data_fp (ctx), ignore_cache);
|
||||
if (rc)
|
||||
log_error ("command pksign failed: %s\n", gnupg_strerror (rc));
|
||||
return map_to_assuan_status (rc);
|
||||
}
|
||||
|
||||
/* PKDECRYPT <options>
|
||||
|
||||
Perform the actual decrypt operation. Input is not
|
||||
sensitive to eavesdropping */
|
||||
static int
|
||||
cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line)
|
||||
{
|
||||
int rc;
|
||||
CTRL ctrl = assuan_get_pointer (ctx);
|
||||
char *value;
|
||||
size_t valuelen;
|
||||
|
||||
/* First inquire the data to decrypt */
|
||||
rc = assuan_inquire (ctx, "CIPHERTEXT",
|
||||
&value, &valuelen, MAXLEN_CIPHERTEXT);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = agent_pkdecrypt (ctrl, value, valuelen, assuan_get_data_fp (ctx));
|
||||
xfree (value);
|
||||
if (rc)
|
||||
log_error ("command pkdecrypt failed: %s\n", gnupg_strerror (rc));
|
||||
return map_to_assuan_status (rc);
|
||||
}
|
||||
|
||||
|
||||
/* GENKEY
|
||||
|
||||
Generate a new key, store the secret part and return the public
|
||||
part. Here is an example transaction:
|
||||
|
||||
C: GENKEY
|
||||
S: INQUIRE KEYPARM
|
||||
C: D (genkey (rsa (nbits 1024)))
|
||||
C: END
|
||||
S: D (public-key
|
||||
S: D (rsa (n 326487324683264) (e 10001)))
|
||||
S OK key created
|
||||
*/
|
||||
|
||||
static int
|
||||
cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
|
||||
{
|
||||
CTRL ctrl = assuan_get_pointer (ctx);
|
||||
int rc;
|
||||
char *value;
|
||||
size_t valuelen;
|
||||
|
||||
/* First inquire the parameters */
|
||||
rc = assuan_inquire (ctx, "KEYPARAM", &value, &valuelen, MAXLEN_KEYPARAM);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = agent_genkey (ctrl, value, valuelen, assuan_get_data_fp (ctx));
|
||||
xfree (value);
|
||||
if (rc)
|
||||
log_error ("command genkey failed: %s\n", gnupg_strerror (rc));
|
||||
return map_to_assuan_status (rc);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
plus_to_blank (char *s)
|
||||
{
|
||||
for (; *s; s++)
|
||||
{
|
||||
if (*s == '+')
|
||||
*s = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
/* GET_PASSPHRASE <cache_id> [<error_message> <prompt> <description>]
|
||||
|
||||
This function is usually used to ask for a passphrase to be used
|
||||
for conventional encryption, but may also be used by programs which
|
||||
need specal handling of passphrases. This command uses a syntax
|
||||
which helps clients to use the agent with minimum effort. The
|
||||
agent either returns with an error or with a OK followed by the hex
|
||||
encoded passphrase. Note that the length of the strings is
|
||||
implicitly limited by the maximum length of a command.
|
||||
*/
|
||||
|
||||
static int
|
||||
cmd_get_passphrase (ASSUAN_CONTEXT ctx, char *line)
|
||||
{
|
||||
int rc;
|
||||
const char *pw;
|
||||
char *response;
|
||||
char *cacheid = NULL, *desc = NULL, *prompt = NULL, *errtext = NULL;
|
||||
char *p;
|
||||
void *cache_marker;
|
||||
|
||||
/* parse the stuff */
|
||||
for (p=line; *p == ' '; p++)
|
||||
;
|
||||
cacheid = p;
|
||||
p = strchr (cacheid, ' ');
|
||||
if (p)
|
||||
{
|
||||
*p++ = 0;
|
||||
while (*p == ' ')
|
||||
p++;
|
||||
errtext = p;
|
||||
p = strchr (errtext, ' ');
|
||||
if (p)
|
||||
{
|
||||
*p++ = 0;
|
||||
while (*p == ' ')
|
||||
p++;
|
||||
prompt = p;
|
||||
p = strchr (prompt, ' ');
|
||||
if (p)
|
||||
{
|
||||
*p++ = 0;
|
||||
while (*p == ' ')
|
||||
p++;
|
||||
desc = p;
|
||||
p = strchr (desc, ' ');
|
||||
if (p)
|
||||
*p = 0; /* ignore garbage */
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!cacheid || !*cacheid || strlen (cacheid) > 50)
|
||||
return set_error (Parameter_Error, "invalid length of cacheID");
|
||||
if (!desc)
|
||||
return set_error (Parameter_Error, "no description given");
|
||||
|
||||
if (!strcmp (cacheid, "X"))
|
||||
cacheid = NULL;
|
||||
if (!strcmp (errtext, "X"))
|
||||
errtext = NULL;
|
||||
if (!strcmp (prompt, "X"))
|
||||
prompt = NULL;
|
||||
if (!strcmp (desc, "X"))
|
||||
desc = NULL;
|
||||
|
||||
/* Note: we store the hexified versions in the cache. */
|
||||
pw = cacheid ? agent_get_cache (cacheid, &cache_marker) : NULL;
|
||||
if (pw)
|
||||
{
|
||||
assuan_begin_confidential (ctx);
|
||||
rc = assuan_set_okay_line (ctx, pw);
|
||||
agent_unlock_cache_entry (&cache_marker);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Note, that we only need to replace the + characters and
|
||||
should leave the other escaping in place because the escaped
|
||||
string is send verbatim to the pinentry which does the
|
||||
unescaping (but not the + replacing) */
|
||||
if (errtext)
|
||||
plus_to_blank (errtext);
|
||||
if (prompt)
|
||||
plus_to_blank (prompt);
|
||||
if (desc)
|
||||
plus_to_blank (desc);
|
||||
|
||||
rc = agent_get_passphrase (&response, desc, prompt, errtext);
|
||||
if (!rc)
|
||||
{
|
||||
if (cacheid)
|
||||
agent_put_cache (cacheid, response, 0);
|
||||
assuan_begin_confidential (ctx);
|
||||
rc = assuan_set_okay_line (ctx, response);
|
||||
xfree (response);
|
||||
}
|
||||
}
|
||||
|
||||
if (rc)
|
||||
log_error ("command get_passphrase failed: %s\n", gnupg_strerror (rc));
|
||||
return map_to_assuan_status (rc);
|
||||
}
|
||||
|
||||
|
||||
/* CLEAR_PASSPHRASE <cache_id>
|
||||
|
||||
may be used to invalidate the cache entry for a passphrase. The
|
||||
function returns with OK even when there is no cached passphrase.
|
||||
*/
|
||||
|
||||
static int
|
||||
cmd_clear_passphrase (ASSUAN_CONTEXT ctx, char *line)
|
||||
{
|
||||
char *cacheid = NULL;
|
||||
char *p;
|
||||
|
||||
/* parse the stuff */
|
||||
for (p=line; *p == ' '; p++)
|
||||
;
|
||||
cacheid = p;
|
||||
p = strchr (cacheid, ' ');
|
||||
if (p)
|
||||
*p = 0; /* ignore garbage */
|
||||
if (!cacheid || !*cacheid || strlen (cacheid) > 50)
|
||||
return set_error (Parameter_Error, "invalid length of cacheID");
|
||||
|
||||
agent_put_cache (cacheid, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* LEARN [--send]
|
||||
|
||||
Learn something about the currently inserted smartcard. With
|
||||
--send the new certificates are send back. */
|
||||
static int
|
||||
cmd_learn (ASSUAN_CONTEXT ctx, char *line)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = agent_handle_learn (has_option (line, "--send")? ctx : NULL);
|
||||
if (rc)
|
||||
log_error ("command learn failed: %s\n", gnupg_strerror (rc));
|
||||
return map_to_assuan_status (rc);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int
|
||||
option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
|
||||
{
|
||||
CTRL ctrl = assuan_get_pointer (ctx);
|
||||
|
||||
/* FIXME: We should not change opt. here. It is not a problem right
|
||||
now but as soon as we are allowing concurrent connections we mess
|
||||
things up */
|
||||
if (!strcmp (key, "display"))
|
||||
{
|
||||
if (opt.display)
|
||||
free (opt.display);
|
||||
opt.display = strdup (value);
|
||||
if (!opt.display)
|
||||
return ASSUAN_Out_Of_Core;
|
||||
}
|
||||
else if (!strcmp (key, "ttyname"))
|
||||
{
|
||||
if (opt.ttyname)
|
||||
free (opt.ttyname);
|
||||
opt.ttyname = strdup (value);
|
||||
if (!opt.ttyname)
|
||||
return ASSUAN_Out_Of_Core;
|
||||
}
|
||||
else if (!strcmp (key, "ttytype"))
|
||||
{
|
||||
if (opt.ttytype)
|
||||
free (opt.ttytype);
|
||||
opt.ttytype = strdup (value);
|
||||
if (!opt.ttytype)
|
||||
return ASSUAN_Out_Of_Core;
|
||||
}
|
||||
else if (!strcmp (key, "lc-ctype"))
|
||||
{
|
||||
if (opt.lc_ctype)
|
||||
free (opt.lc_ctype);
|
||||
opt.lc_ctype = strdup (value);
|
||||
if (!opt.lc_ctype)
|
||||
return ASSUAN_Out_Of_Core;
|
||||
}
|
||||
else if (!strcmp (key, "lc-messages"))
|
||||
{
|
||||
if (opt.lc_messages)
|
||||
free (opt.lc_messages);
|
||||
opt.lc_messages = strdup (value);
|
||||
if (!opt.lc_messages)
|
||||
return ASSUAN_Out_Of_Core;
|
||||
}
|
||||
else if (!strcmp (key, "use-cache-for-signing"))
|
||||
ctrl->server_local->use_cache_for_signing = *value? atoi (value) : 0;
|
||||
else
|
||||
return ASSUAN_Invalid_Option;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Tell the assuan library about our commands */
|
||||
static int
|
||||
register_commands (ASSUAN_CONTEXT ctx)
|
||||
{
|
||||
static struct {
|
||||
const char *name;
|
||||
int cmd_id;
|
||||
int (*handler)(ASSUAN_CONTEXT, char *line);
|
||||
} table[] = {
|
||||
{ "ISTRUSTED", 0, cmd_istrusted },
|
||||
{ "HAVEKEY", 0, cmd_havekey },
|
||||
{ "SIGKEY", 0, cmd_sigkey },
|
||||
{ "SETKEY", 0, cmd_sigkey },
|
||||
{ "SETHASH", 0, cmd_sethash },
|
||||
{ "PKSIGN", 0, cmd_pksign },
|
||||
{ "PKDECRYPT", 0, cmd_pkdecrypt },
|
||||
{ "GENKEY", 0, cmd_genkey },
|
||||
{ "GET_PASSPHRASE",0, cmd_get_passphrase },
|
||||
{ "CLEAR_PASSPHRASE",0, cmd_clear_passphrase },
|
||||
{ "LISTTRUSTED", 0, cmd_listtrusted },
|
||||
{ "MARKTRUSTED", 0, cmd_marktrusted },
|
||||
{ "LEARN", 0, cmd_learn },
|
||||
{ "", ASSUAN_CMD_INPUT, NULL },
|
||||
{ "", ASSUAN_CMD_OUTPUT, NULL },
|
||||
{ NULL }
|
||||
};
|
||||
int i, j, rc;
|
||||
|
||||
for (i=j=0; table[i].name; i++)
|
||||
{
|
||||
rc = assuan_register_command (ctx,
|
||||
table[i].cmd_id? table[i].cmd_id
|
||||
: (ASSUAN_CMD_USER + j++),
|
||||
table[i].name, table[i].handler);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
assuan_register_reset_notify (ctx, reset_notify);
|
||||
assuan_register_option_handler (ctx, option_handler);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Startup the server. If LISTEN_FD and FD is given as -1, this is a simple
|
||||
piper server, otherwise it is a regular server */
|
||||
void
|
||||
start_command_handler (int listen_fd, int fd)
|
||||
{
|
||||
int rc;
|
||||
ASSUAN_CONTEXT ctx;
|
||||
struct server_control_s ctrl;
|
||||
|
||||
memset (&ctrl, 0, sizeof ctrl);
|
||||
|
||||
if (listen_fd == -1 && fd == -1)
|
||||
{
|
||||
int filedes[2];
|
||||
|
||||
filedes[0] = 0;
|
||||
filedes[1] = 1;
|
||||
rc = assuan_init_pipe_server (&ctx, filedes);
|
||||
}
|
||||
else if (listen_fd != -1)
|
||||
{
|
||||
rc = assuan_init_socket_server (&ctx, listen_fd);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = assuan_init_connected_socket_server (&ctx, fd);
|
||||
}
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to initialize the server: %s\n",
|
||||
assuan_strerror(rc));
|
||||
agent_exit (2);
|
||||
}
|
||||
rc = register_commands (ctx);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to register commands with Assuan: %s\n",
|
||||
assuan_strerror(rc));
|
||||
agent_exit (2);
|
||||
}
|
||||
|
||||
assuan_set_pointer (ctx, &ctrl);
|
||||
ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
|
||||
ctrl.server_local->assuan_ctx = ctx;
|
||||
ctrl.server_local->message_fd = -1;
|
||||
ctrl.server_local->use_cache_for_signing = 1;
|
||||
|
||||
if (DBG_ASSUAN)
|
||||
assuan_set_log_stream (ctx, log_get_stream ());
|
||||
|
||||
for (;;)
|
||||
{
|
||||
rc = assuan_accept (ctx);
|
||||
if (rc == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (rc)
|
||||
{
|
||||
log_info ("Assuan accept problem: %s\n", assuan_strerror (rc));
|
||||
break;
|
||||
}
|
||||
|
||||
rc = assuan_process (ctx);
|
||||
if (rc)
|
||||
{
|
||||
log_info ("Assuan processing failed: %s\n", assuan_strerror (rc));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
assuan_deinit_server (ctx);
|
||||
}
|
||||
|
|
@ -1,305 +0,0 @@
|
|||
/* divert-scd.c - divert operations to the scdaemon
|
||||
* Copyright (C) 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "agent.h"
|
||||
#include "sexp-parse.h"
|
||||
#include "i18n.h"
|
||||
|
||||
|
||||
static int
|
||||
ask_for_card (const unsigned char *shadow_info, char **r_kid)
|
||||
{
|
||||
int rc, i;
|
||||
const unsigned char *s;
|
||||
size_t n;
|
||||
char *serialno;
|
||||
int no_card = 0;
|
||||
char *desc;
|
||||
char *want_sn, *want_kid;
|
||||
int want_sn_displen;
|
||||
|
||||
*r_kid = NULL;
|
||||
s = shadow_info;
|
||||
if (*s != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
want_sn = xtrymalloc (n*2+1);
|
||||
if (!want_sn)
|
||||
return GNUPG_Out_Of_Core;
|
||||
for (i=0; i < n; i++)
|
||||
sprintf (want_sn+2*i, "%02X", s[i]);
|
||||
s += n;
|
||||
/* We assume that a 20 byte serial number is a standard one which
|
||||
seems to have the property to have a zero in the last nibble. We
|
||||
don't display this '0' because it may confuse the user */
|
||||
want_sn_displen = strlen (want_sn);
|
||||
if (want_sn_displen == 20 && want_sn[19] == '0')
|
||||
want_sn_displen--;
|
||||
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
want_kid = xtrymalloc (n+1);
|
||||
if (!want_kid)
|
||||
{
|
||||
xfree (want_sn);
|
||||
return GNUPG_Out_Of_Core;
|
||||
}
|
||||
memcpy (want_kid, s, n);
|
||||
want_kid[n] = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
rc = agent_card_serialno (&serialno);
|
||||
if (!rc)
|
||||
{
|
||||
log_debug ("detected card with S/N %s\n", serialno);
|
||||
i = strcmp (serialno, want_sn);
|
||||
xfree (serialno);
|
||||
serialno = NULL;
|
||||
if (!i)
|
||||
{
|
||||
xfree (want_sn);
|
||||
*r_kid = want_kid;
|
||||
return 0; /* yes, we have the correct card */
|
||||
}
|
||||
}
|
||||
else if (rc == GNUPG_Card_Not_Present)
|
||||
{
|
||||
log_debug ("no card present\n");
|
||||
rc = 0;
|
||||
no_card = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_error ("error accesing card: %s\n", gnupg_strerror (rc));
|
||||
}
|
||||
|
||||
if (!rc)
|
||||
{
|
||||
if (asprintf (&desc,
|
||||
"%s:%%0A%%0A"
|
||||
" \"%.*s\"",
|
||||
no_card? "Please insert the card with serial number"
|
||||
: "Please remove the current card and "
|
||||
"insert the one with serial number",
|
||||
want_sn_displen, want_sn) < 0)
|
||||
{
|
||||
rc = GNUPG_Out_Of_Core;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = agent_get_confirmation (desc, NULL, NULL);
|
||||
free (desc);
|
||||
}
|
||||
}
|
||||
if (rc)
|
||||
{
|
||||
xfree (want_sn);
|
||||
xfree (want_kid);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Put the DIGEST into an DER encoded comtainer and return it in R_VAL. */
|
||||
static int
|
||||
encode_md_for_card (const unsigned char *digest, size_t digestlen, int algo,
|
||||
unsigned char **r_val, size_t *r_len)
|
||||
{
|
||||
byte *frame;
|
||||
byte asn[100];
|
||||
size_t asnlen;
|
||||
|
||||
asnlen = DIM(asn);
|
||||
if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
|
||||
{
|
||||
log_error ("no object identifier for algo %d\n", algo);
|
||||
return GNUPG_Internal_Error;
|
||||
}
|
||||
|
||||
frame = xtrymalloc (asnlen + digestlen);
|
||||
if (!frame)
|
||||
return GNUPG_Out_Of_Core;
|
||||
memcpy (frame, asn, asnlen);
|
||||
memcpy (frame+asnlen, digest, digestlen);
|
||||
if (DBG_CRYPTO)
|
||||
log_printhex ("encoded hash:", frame, asnlen+digestlen);
|
||||
|
||||
*r_val = frame;
|
||||
*r_len = asnlen+digestlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Callback used to ask for the PIN which should be set into BUF. The
|
||||
buf has been allocated by the caller and is of size MAXBUF which
|
||||
includes the terminating null. The function should return an UTF-8
|
||||
string with the passphrase, the buffer may optionally be padded
|
||||
with arbitrary characters */
|
||||
static int
|
||||
getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
|
||||
{
|
||||
struct pin_entry_info_s *pi;
|
||||
int rc;
|
||||
char *desc;
|
||||
|
||||
assert (!opaque);
|
||||
|
||||
if (maxbuf < 2)
|
||||
return GNUPG_Invalid_Value;
|
||||
|
||||
|
||||
/* FIXME: keep PI and TRIES in OPAQUE. Frankly this is a whole
|
||||
mess because we should call the card's verify function from the
|
||||
pinentry check pin CB. */
|
||||
pi = gcry_calloc_secure (1, sizeof (*pi) + 100);
|
||||
pi->max_length = maxbuf-1;
|
||||
pi->min_digits = 0; /* we want a real passphrase */
|
||||
pi->max_digits = 8;
|
||||
pi->max_tries = 3;
|
||||
|
||||
if ( asprintf (&desc, _("Please enter the PIN%s%s%s to unlock the card"),
|
||||
info? " (`":"",
|
||||
info? info:"",
|
||||
info? "')":"") < 0)
|
||||
desc = NULL;
|
||||
rc = agent_askpin (desc?desc:info, pi);
|
||||
free (desc);
|
||||
if (!rc)
|
||||
{
|
||||
strncpy (buf, pi->pin, maxbuf-1);
|
||||
buf[maxbuf-1] = 0;
|
||||
}
|
||||
xfree (pi);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int
|
||||
divert_pksign (const unsigned char *digest, size_t digestlen, int algo,
|
||||
const unsigned char *shadow_info, unsigned char **r_sig)
|
||||
{
|
||||
int rc;
|
||||
char *kid;
|
||||
size_t siglen;
|
||||
char *sigval;
|
||||
unsigned char *data;
|
||||
size_t ndata;
|
||||
|
||||
rc = ask_for_card (shadow_info, &kid);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = encode_md_for_card (digest, digestlen, algo,
|
||||
&data, &ndata);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = agent_card_pksign (kid, getpin_cb, NULL,
|
||||
data, ndata, &sigval, &siglen);
|
||||
if (!rc)
|
||||
*r_sig = sigval;
|
||||
xfree (data);
|
||||
xfree (kid);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Decrypt the the value given asn an S-expression in CIPHER using the
|
||||
key identified by SHADOW_INFO and return the plaintext in an
|
||||
allocated buffer in R_BUF. */
|
||||
int
|
||||
divert_pkdecrypt (const unsigned char *cipher,
|
||||
const unsigned char *shadow_info,
|
||||
char **r_buf, size_t *r_len)
|
||||
{
|
||||
int rc;
|
||||
char *kid;
|
||||
const unsigned char *s;
|
||||
size_t n;
|
||||
const unsigned char *ciphertext;
|
||||
size_t ciphertextlen;
|
||||
char *plaintext;
|
||||
size_t plaintextlen;
|
||||
|
||||
s = cipher;
|
||||
if (*s != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (!smatch (&s, n, "enc-val"))
|
||||
return GNUPG_Unknown_Sexp;
|
||||
if (*s != '(')
|
||||
return GNUPG_Unknown_Sexp;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (!smatch (&s, n, "rsa"))
|
||||
return GNUPG_Unsupported_Algorithm;
|
||||
if (*s != '(')
|
||||
return GNUPG_Unknown_Sexp;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (!smatch (&s, n, "a"))
|
||||
return GNUPG_Unknown_Sexp;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Unknown_Sexp;
|
||||
ciphertext = s;
|
||||
ciphertextlen = n;
|
||||
|
||||
rc = ask_for_card (shadow_info, &kid);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = agent_card_pkdecrypt (kid, getpin_cb, NULL,
|
||||
ciphertext, ciphertextlen,
|
||||
&plaintext, &plaintextlen);
|
||||
if (!rc)
|
||||
{
|
||||
*r_buf = plaintext;
|
||||
*r_len = plaintextlen;
|
||||
}
|
||||
xfree (kid);
|
||||
return rc;
|
||||
}
|
350
agent/findkey.c
350
agent/findkey.c
|
@ -1,350 +0,0 @@
|
|||
/* findkey.c - locate the secret key
|
||||
* Copyright (C) 2001,02 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "agent.h"
|
||||
|
||||
/* Helper to pass data to the check callback of the unprotect function. */
|
||||
struct try_unprotect_arg_s {
|
||||
const unsigned char *protected_key;
|
||||
unsigned char *unprotected_key;
|
||||
};
|
||||
|
||||
|
||||
|
||||
int
|
||||
agent_write_private_key (const unsigned char *grip,
|
||||
const void *buffer, size_t length, int force)
|
||||
{
|
||||
int i;
|
||||
char *fname;
|
||||
FILE *fp;
|
||||
char hexgrip[40+4+1];
|
||||
|
||||
for (i=0; i < 20; i++)
|
||||
sprintf (hexgrip+2*i, "%02X", grip[i]);
|
||||
strcpy (hexgrip+40, ".key");
|
||||
|
||||
fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
|
||||
if (force)
|
||||
fp = fopen (fname, "wb");
|
||||
else
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (!access (fname, F_OK))
|
||||
{
|
||||
log_error ("secret key file `%s' already exists\n", fname);
|
||||
xfree (fname);
|
||||
return seterr (General_Error);
|
||||
}
|
||||
|
||||
/* We would like to create FNAME but only if it does not already
|
||||
exist. We cannot make this guarantee just using POSIX (GNU
|
||||
provides the "x" opentype for fopen, however, this is not
|
||||
portable). Thus, we use the more flexible open function and
|
||||
then use fdopen to obtain a stream.
|
||||
|
||||
The mode parameter to open is what fopen uses. It will be
|
||||
combined with the process' umask automatically. */
|
||||
fd = open (fname, O_CREAT | O_EXCL | O_RDWR,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||
if (fd < 0)
|
||||
fp = 0;
|
||||
else
|
||||
{
|
||||
fp = fdopen (fd, "wb");
|
||||
if (! fp)
|
||||
close (fd);
|
||||
}
|
||||
}
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
log_error ("can't create `%s': %s\n", fname, strerror (errno));
|
||||
xfree (fname);
|
||||
return seterr (File_Create_Error);
|
||||
}
|
||||
|
||||
if (fwrite (buffer, length, 1, fp) != 1)
|
||||
{
|
||||
log_error ("error writing `%s': %s\n", fname, strerror (errno));
|
||||
fclose (fp);
|
||||
remove (fname);
|
||||
xfree (fname);
|
||||
return seterr (File_Create_Error);
|
||||
}
|
||||
if ( fclose (fp) )
|
||||
{
|
||||
log_error ("error closing `%s': %s\n", fname, strerror (errno));
|
||||
remove (fname);
|
||||
xfree (fname);
|
||||
return seterr (File_Create_Error);
|
||||
}
|
||||
|
||||
xfree (fname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Callback function to try the unprotection from the passpharse query
|
||||
code. */
|
||||
static int
|
||||
try_unprotect_cb (struct pin_entry_info_s *pi)
|
||||
{
|
||||
struct try_unprotect_arg_s *arg = pi->check_cb_arg;
|
||||
size_t dummy;
|
||||
|
||||
assert (!arg->unprotected_key);
|
||||
return agent_unprotect (arg->protected_key, pi->pin,
|
||||
&arg->unprotected_key, &dummy);
|
||||
}
|
||||
|
||||
|
||||
/* Unprotect the canconical encoded S-expression key in KEYBUF. GRIP
|
||||
should be the hex encoded keygrip of that key to be used with the
|
||||
cahing mechanism. */
|
||||
static int
|
||||
unprotect (unsigned char **keybuf, const unsigned char *grip, int ignore_cache)
|
||||
{
|
||||
struct pin_entry_info_s *pi;
|
||||
struct try_unprotect_arg_s arg;
|
||||
int rc, i;
|
||||
unsigned char *result;
|
||||
size_t resultlen;
|
||||
char hexgrip[40+1];
|
||||
|
||||
for (i=0; i < 20; i++)
|
||||
sprintf (hexgrip+2*i, "%02X", grip[i]);
|
||||
hexgrip[40] = 0;
|
||||
|
||||
/* first try to get it from the cache - if there is none or we can't
|
||||
unprotect it, we fall back to ask the user */
|
||||
if (!ignore_cache)
|
||||
{
|
||||
void *cache_marker;
|
||||
const char *pw = agent_get_cache (hexgrip, &cache_marker);
|
||||
if (pw)
|
||||
{
|
||||
rc = agent_unprotect (*keybuf, pw, &result, &resultlen);
|
||||
agent_unlock_cache_entry (&cache_marker);
|
||||
if (!rc)
|
||||
{
|
||||
xfree (*keybuf);
|
||||
*keybuf = result;
|
||||
return 0;
|
||||
}
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pi = gcry_calloc_secure (1, sizeof (*pi) + 100);
|
||||
pi->max_length = 100;
|
||||
pi->min_digits = 0; /* we want a real passphrase */
|
||||
pi->max_digits = 8;
|
||||
pi->max_tries = 3;
|
||||
pi->check_cb = try_unprotect_cb;
|
||||
arg.protected_key = *keybuf;
|
||||
arg.unprotected_key = NULL;
|
||||
pi->check_cb_arg = &arg;
|
||||
|
||||
rc = agent_askpin (NULL, pi);
|
||||
if (!rc)
|
||||
{
|
||||
assert (arg.unprotected_key);
|
||||
agent_put_cache (hexgrip, pi->pin, 0);
|
||||
xfree (*keybuf);
|
||||
*keybuf = arg.unprotected_key;
|
||||
}
|
||||
xfree (pi);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Return the secret key as an S-Exp after locating it using the grip.
|
||||
Returns NULL if key is not available or the operation should be
|
||||
diverted to a token. In the latter case shadow_info will point to
|
||||
an allocated S-Expression with the shadow_info part from the file.
|
||||
With IGNORE_CACHE passed as true the passphrase is not taken from
|
||||
the cache.*/
|
||||
GCRY_SEXP
|
||||
agent_key_from_file (const unsigned char *grip, unsigned char **shadow_info,
|
||||
int ignore_cache)
|
||||
{
|
||||
int i, rc;
|
||||
char *fname;
|
||||
FILE *fp;
|
||||
struct stat st;
|
||||
unsigned char *buf;
|
||||
size_t len, buflen, erroff;
|
||||
GCRY_SEXP s_skey;
|
||||
char hexgrip[40+4+1];
|
||||
|
||||
if (shadow_info)
|
||||
*shadow_info = NULL;
|
||||
|
||||
for (i=0; i < 20; i++)
|
||||
sprintf (hexgrip+2*i, "%02X", grip[i]);
|
||||
strcpy (hexgrip+40, ".key");
|
||||
|
||||
fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
|
||||
fp = fopen (fname, "rb");
|
||||
if (!fp)
|
||||
{
|
||||
log_error ("can't open `%s': %s\n", fname, strerror (errno));
|
||||
xfree (fname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fstat (fileno(fp), &st))
|
||||
{
|
||||
log_error ("can't stat `%s': %s\n", fname, strerror (errno));
|
||||
xfree (fname);
|
||||
fclose (fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buflen = st.st_size;
|
||||
buf = xmalloc (buflen+1);
|
||||
if (fread (buf, buflen, 1, fp) != 1)
|
||||
{
|
||||
log_error ("error reading `%s': %s\n", fname, strerror (errno));
|
||||
xfree (fname);
|
||||
fclose (fp);
|
||||
xfree (buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc = gcry_sexp_sscan (&s_skey, &erroff, buf, buflen);
|
||||
xfree (fname);
|
||||
fclose (fp);
|
||||
xfree (buf);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to build S-Exp (off=%u): %s\n",
|
||||
(unsigned int)erroff, gcry_strerror (rc));
|
||||
return NULL;
|
||||
}
|
||||
len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0);
|
||||
assert (len);
|
||||
buf = xtrymalloc (len);
|
||||
if (!buf)
|
||||
{
|
||||
gcry_sexp_release (s_skey);
|
||||
return NULL;
|
||||
}
|
||||
len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, buf, len);
|
||||
assert (len);
|
||||
gcry_sexp_release (s_skey);
|
||||
|
||||
switch (agent_private_key_type (buf))
|
||||
{
|
||||
case PRIVATE_KEY_CLEAR:
|
||||
break; /* no unprotection needed */
|
||||
case PRIVATE_KEY_PROTECTED:
|
||||
rc = unprotect (&buf, grip, ignore_cache);
|
||||
if (rc)
|
||||
log_error ("failed to unprotect the secret key: %s\n",
|
||||
gnupg_strerror (rc));
|
||||
break;
|
||||
case PRIVATE_KEY_SHADOWED:
|
||||
if (shadow_info)
|
||||
{
|
||||
const unsigned char *s;
|
||||
size_t n;
|
||||
|
||||
rc = agent_get_shadow_info (buf, &s);
|
||||
if (!rc)
|
||||
{
|
||||
n = gcry_sexp_canon_len (s, 0, NULL,NULL);
|
||||
assert (n);
|
||||
*shadow_info = xtrymalloc (n);
|
||||
if (!*shadow_info)
|
||||
rc = GNUPG_Out_Of_Core;
|
||||
else
|
||||
{
|
||||
memcpy (*shadow_info, s, n);
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
if (rc)
|
||||
log_error ("get_shadow_info failed: %s\n", gnupg_strerror (rc));
|
||||
}
|
||||
rc = -1; /* ugly interface: we return an error but keep a value
|
||||
in shadow_info. */
|
||||
break;
|
||||
default:
|
||||
log_error ("invalid private key format\n");
|
||||
rc = GNUPG_Bad_Secret_Key;
|
||||
break;
|
||||
}
|
||||
if (rc)
|
||||
{
|
||||
xfree (buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* arggg FIXME: does scan support secure memory? */
|
||||
rc = gcry_sexp_sscan (&s_skey, &erroff,
|
||||
buf, gcry_sexp_canon_len (buf, 0, NULL, NULL));
|
||||
xfree (buf);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to build S-Exp (off=%u): %s\n",
|
||||
(unsigned int)erroff, gcry_strerror (rc));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return s_skey;
|
||||
}
|
||||
|
||||
/* Return the secret key as an S-Exp after locating it using the grip.
|
||||
Returns NULL if key is not available. 0 = key is available */
|
||||
int
|
||||
agent_key_available (const unsigned char *grip)
|
||||
{
|
||||
int i;
|
||||
char *fname;
|
||||
char hexgrip[40+4+1];
|
||||
|
||||
for (i=0; i < 20; i++)
|
||||
sprintf (hexgrip+2*i, "%02X", grip[i]);
|
||||
strcpy (hexgrip+40, ".key");
|
||||
|
||||
fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
|
||||
i = !access (fname, R_OK)? 0 : -1;
|
||||
xfree (fname);
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
|
200
agent/genkey.c
200
agent/genkey.c
|
@ -1,200 +0,0 @@
|
|||
/* pksign.c - Generate a keypair
|
||||
* Copyright (C) 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "agent.h"
|
||||
#include "i18n.h"
|
||||
|
||||
static int
|
||||
store_key (GCRY_SEXP private, const char *passphrase)
|
||||
{
|
||||
int rc;
|
||||
char *buf;
|
||||
size_t len;
|
||||
unsigned char grip[20];
|
||||
|
||||
if ( !gcry_pk_get_keygrip (private, grip) )
|
||||
{
|
||||
log_error ("can't calculate keygrip\n");
|
||||
return seterr (General_Error);
|
||||
}
|
||||
|
||||
len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, NULL, 0);
|
||||
assert (len);
|
||||
buf = gcry_malloc_secure (len);
|
||||
if (!buf)
|
||||
return seterr (Out_Of_Core);
|
||||
len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, buf, len);
|
||||
assert (len);
|
||||
|
||||
if (passphrase)
|
||||
{
|
||||
unsigned char *p;
|
||||
|
||||
rc = agent_protect (buf, passphrase, &p, &len);
|
||||
if (rc)
|
||||
{
|
||||
xfree (buf);
|
||||
return rc;
|
||||
}
|
||||
xfree (buf);
|
||||
buf = p;
|
||||
}
|
||||
|
||||
rc = agent_write_private_key (grip, buf, len, 0);
|
||||
xfree (buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Callback function to compare the first entered PIN with the one
|
||||
currently beeing entered. */
|
||||
static int
|
||||
reenter_compare_cb (struct pin_entry_info_s *pi)
|
||||
{
|
||||
const char *pin1 = pi->check_cb_arg;
|
||||
|
||||
if (!strcmp (pin1, pi->pin))
|
||||
return 0; /* okay */
|
||||
pi->cb_errtext = _("does not match - try again");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Generate a new keypair according to the parameters given in
|
||||
KEYPARAM */
|
||||
int
|
||||
agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparamlen,
|
||||
FILE *outfp)
|
||||
{
|
||||
GCRY_SEXP s_keyparam, s_key, s_private, s_public;
|
||||
struct pin_entry_info_s *pi, *pi2;
|
||||
int rc;
|
||||
size_t len;
|
||||
char *buf;
|
||||
|
||||
rc = gcry_sexp_sscan (&s_keyparam, NULL, keyparam, keyparamlen);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to convert keyparam: %s\n", gcry_strerror (rc));
|
||||
return seterr (Invalid_Data);
|
||||
}
|
||||
|
||||
/* Get the passphrase now, cause key generation may take a while. */
|
||||
{
|
||||
const char *text1 = _("Please enter the passphrase to%0A"
|
||||
"to protect your new key");
|
||||
const char *text2 = _("Please re-enter this passphrase");
|
||||
|
||||
pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
|
||||
pi2 = pi + (sizeof *pi + 100);
|
||||
pi->max_length = 100;
|
||||
pi->max_tries = 3;
|
||||
pi2->max_length = 100;
|
||||
pi2->max_tries = 3;
|
||||
pi2->check_cb = reenter_compare_cb;
|
||||
pi2->check_cb_arg = pi->pin;
|
||||
|
||||
rc = agent_askpin (text1, pi);
|
||||
if (!rc)
|
||||
rc = agent_askpin (text2, pi2);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (!*pi->pin)
|
||||
{
|
||||
xfree (pi);
|
||||
pi = NULL; /* User does not want a passphrase. */
|
||||
}
|
||||
}
|
||||
|
||||
rc = gcry_pk_genkey (&s_key, s_keyparam );
|
||||
gcry_sexp_release (s_keyparam);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("key generation failed: %s\n", gcry_strerror (rc));
|
||||
xfree (pi);
|
||||
return map_gcry_err (rc);
|
||||
}
|
||||
|
||||
/* break out the parts */
|
||||
s_private = gcry_sexp_find_token (s_key, "private-key", 0);
|
||||
if (!s_private)
|
||||
{
|
||||
log_error ("key generation failed: invalid return value\n");
|
||||
gcry_sexp_release (s_key);
|
||||
xfree (pi);
|
||||
return seterr (Invalid_Data);
|
||||
}
|
||||
s_public = gcry_sexp_find_token (s_key, "public-key", 0);
|
||||
if (!s_public)
|
||||
{
|
||||
log_error ("key generation failed: invalid return value\n");
|
||||
gcry_sexp_release (s_private);
|
||||
gcry_sexp_release (s_key);
|
||||
xfree (pi);
|
||||
return seterr (Invalid_Data);
|
||||
}
|
||||
gcry_sexp_release (s_key); s_key = NULL;
|
||||
|
||||
/* store the secret key */
|
||||
log_debug ("storing private key\n");
|
||||
rc = store_key (s_private, pi? pi->pin:NULL);
|
||||
xfree (pi); pi = NULL;
|
||||
gcry_sexp_release (s_private);
|
||||
if (rc)
|
||||
{
|
||||
gcry_sexp_release (s_public);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* return the public key */
|
||||
log_debug ("returning public key\n");
|
||||
len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, NULL, 0);
|
||||
assert (len);
|
||||
buf = xmalloc (len);
|
||||
if (!buf)
|
||||
{
|
||||
gcry_sexp_release (s_private);
|
||||
gcry_sexp_release (s_public);
|
||||
return seterr (Out_Of_Core);
|
||||
}
|
||||
len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, buf, len);
|
||||
assert (len);
|
||||
if (fwrite (buf, len, 1, outfp) != 1)
|
||||
{
|
||||
log_error ("error writing public key: %s\n", strerror (errno));
|
||||
gcry_sexp_release (s_private);
|
||||
gcry_sexp_release (s_public);
|
||||
xfree (buf);
|
||||
return seterr (File_Create_Error);
|
||||
}
|
||||
gcry_sexp_release (s_public);
|
||||
xfree (buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,916 +0,0 @@
|
|||
/* gpg-agent.c - The GnuPG Agent
|
||||
* Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#ifdef USE_GNU_PTH
|
||||
# include <pth.h>
|
||||
#endif
|
||||
|
||||
#include <gcrypt.h>
|
||||
|
||||
#define JNLIB_NEED_LOG_LOGV
|
||||
#include "agent.h"
|
||||
#include "../assuan/assuan.h" /* malloc hooks */
|
||||
|
||||
#include "i18n.h"
|
||||
#include "sysutils.h"
|
||||
|
||||
|
||||
enum cmd_and_opt_values
|
||||
{ aNull = 0,
|
||||
oCsh = 'c',
|
||||
oQuiet = 'q',
|
||||
oSh = 's',
|
||||
oVerbose = 'v',
|
||||
|
||||
oNoVerbose = 500,
|
||||
oOptions,
|
||||
oDebug,
|
||||
oDebugAll,
|
||||
oDebugWait,
|
||||
oNoGreeting,
|
||||
oNoOptions,
|
||||
oHomedir,
|
||||
oNoDetach,
|
||||
oNoGrab,
|
||||
oLogFile,
|
||||
oServer,
|
||||
oDaemon,
|
||||
oBatch,
|
||||
|
||||
oPinentryProgram,
|
||||
oDisplay,
|
||||
oTTYname,
|
||||
oTTYtype,
|
||||
oLCctype,
|
||||
oLCmessages,
|
||||
oScdaemonProgram,
|
||||
oDefCacheTTL,
|
||||
oDisablePth,
|
||||
|
||||
oIgnoreCacheForSigning,
|
||||
oKeepTTY,
|
||||
oKeepDISPLAY,
|
||||
|
||||
aTest };
|
||||
|
||||
|
||||
|
||||
static ARGPARSE_OPTS opts[] = {
|
||||
|
||||
{ 301, NULL, 0, N_("@Options:\n ") },
|
||||
|
||||
{ oServer, "server", 0, N_("run in server mode (foreground)") },
|
||||
{ oDaemon, "daemon", 0, N_("run in daemon mode (background)") },
|
||||
{ oVerbose, "verbose", 0, N_("verbose") },
|
||||
{ oQuiet, "quiet", 0, N_("be somewhat more quiet") },
|
||||
{ oSh, "sh", 0, N_("sh-style command output") },
|
||||
{ oCsh, "csh", 0, N_("csh-style command output") },
|
||||
{ oOptions, "options" , 2, N_("read options from file")},
|
||||
{ oDebug, "debug" ,4|16, N_("set debugging flags")},
|
||||
{ oDebugAll, "debug-all" ,0, N_("enable full debugging")},
|
||||
{ oDebugWait,"debug-wait",1, "@"},
|
||||
{ oNoDetach, "no-detach" ,0, N_("do not detach from the console")},
|
||||
{ oNoGrab, "no-grab" ,0, N_("do not grab keyboard and mouse")},
|
||||
{ oLogFile, "log-file" ,2, N_("use a log file for the server")},
|
||||
{ oDisablePth, "disable-pth", 0, N_("do not allow multiple connections")},
|
||||
|
||||
{ oPinentryProgram, "pinentry-program", 2 , "path to PIN Entry program" },
|
||||
{ oDisplay, "display", 2, "set the display" },
|
||||
{ oTTYname, "ttyname", 2, "set the tty terminal node name" },
|
||||
{ oTTYtype, "ttytype", 2, "set the tty terminal type" },
|
||||
{ oLCctype, "lc-ctype", 2, "set the tty LC_CTYPE value" },
|
||||
{ oLCmessages, "lc-messages", 2, "set the tty LC_MESSAGES value" },
|
||||
|
||||
{ oScdaemonProgram, "scdaemon-program", 2 , "path to SCdaemon program" },
|
||||
{ oDefCacheTTL, "default-cache-ttl", 4,
|
||||
"|N|expire cached PINs after N seconds"},
|
||||
{ oIgnoreCacheForSigning, "ignore-cache-for-signing", 0,
|
||||
"do not use the PIN cache when signing"},
|
||||
{ oKeepTTY, "keep-tty", 0, N_("ignore requests to change the TTY")},
|
||||
{ oKeepDISPLAY, "keep-display",
|
||||
0, N_("ignore requests to change the X display")},
|
||||
{0}
|
||||
};
|
||||
|
||||
|
||||
static volatile int caught_fatal_sig = 0;
|
||||
|
||||
/* flag to indicate that a shutdown was requested */
|
||||
static int shutdown_pending;
|
||||
|
||||
|
||||
/* It is possible that we are currently running under setuid permissions */
|
||||
static int maybe_setuid = 1;
|
||||
|
||||
/* Name of the communication socket */
|
||||
static char socket_name[128];
|
||||
|
||||
static void create_directories (void);
|
||||
#ifdef USE_GNU_PTH
|
||||
static void handle_connections (int listen_fd);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
static const char *
|
||||
my_strusage (int level)
|
||||
{
|
||||
const char *p;
|
||||
switch (level)
|
||||
{
|
||||
case 11: p = "gpg-agent (GnuPG)";
|
||||
break;
|
||||
case 13: p = VERSION; break;
|
||||
case 17: p = PRINTABLE_OS_NAME; break;
|
||||
case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
|
||||
break;
|
||||
case 1:
|
||||
case 40: p = _("Usage: gpg-agent [options] (-h for help)");
|
||||
break;
|
||||
case 41: p = _("Syntax: gpg-agent [options] [command [args]]\n"
|
||||
"Secret key management for GnuPG\n");
|
||||
break;
|
||||
|
||||
default: p = NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
i18n_init (void)
|
||||
{
|
||||
#ifdef USE_SIMPLE_GETTEXT
|
||||
set_gettext_file( PACKAGE );
|
||||
#else
|
||||
#ifdef ENABLE_NLS
|
||||
setlocale (LC_ALL, "");
|
||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
||||
textdomain (PACKAGE);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Used by gcry for logging */
|
||||
static void
|
||||
my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
|
||||
{
|
||||
/* translate the log levels */
|
||||
switch (level)
|
||||
{
|
||||
case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break;
|
||||
case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break;
|
||||
case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break;
|
||||
case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break;
|
||||
case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
|
||||
case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break;
|
||||
case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
|
||||
default: level = JNLIB_LOG_ERROR; break;
|
||||
}
|
||||
log_logv (level, fmt, arg_ptr);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
cleanup (void)
|
||||
{
|
||||
if (*socket_name)
|
||||
{
|
||||
char *p;
|
||||
|
||||
remove (socket_name);
|
||||
p = strrchr (socket_name, '/');
|
||||
if (p)
|
||||
{
|
||||
*p = 0;
|
||||
rmdir (socket_name);
|
||||
*p = '/';
|
||||
}
|
||||
*socket_name = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static RETSIGTYPE
|
||||
cleanup_sh (int sig)
|
||||
{
|
||||
if (caught_fatal_sig)
|
||||
raise (sig);
|
||||
caught_fatal_sig = 1;
|
||||
|
||||
/* gcry_control( GCRYCTL_TERM_SECMEM );*/
|
||||
cleanup ();
|
||||
|
||||
#ifndef HAVE_DOSISH_SYSTEM
|
||||
{ /* reset action to default action and raise signal again */
|
||||
struct sigaction nact;
|
||||
nact.sa_handler = SIG_DFL;
|
||||
sigemptyset( &nact.sa_mask );
|
||||
nact.sa_flags = 0;
|
||||
sigaction( sig, &nact, NULL);
|
||||
}
|
||||
#endif
|
||||
raise( sig );
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv )
|
||||
{
|
||||
ARGPARSE_ARGS pargs;
|
||||
int orig_argc;
|
||||
int may_coredump;
|
||||
char **orig_argv;
|
||||
FILE *configfp = NULL;
|
||||
char *configname = NULL;
|
||||
const char *shell;
|
||||
unsigned configlineno;
|
||||
int parse_debug = 0;
|
||||
int default_config =1;
|
||||
int greeting = 0;
|
||||
int nogreeting = 0;
|
||||
int pipe_server = 0;
|
||||
int is_daemon = 0;
|
||||
int nodetach = 0;
|
||||
int csh_style = 0;
|
||||
char *logfile = NULL;
|
||||
int debug_wait = 0;
|
||||
int disable_pth = 0;
|
||||
|
||||
set_strusage (my_strusage);
|
||||
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
|
||||
/* Please note that we may running SUID(ROOT), so be very CAREFUL
|
||||
when adding any stuff between here and the call to INIT_SECMEM()
|
||||
somewhere after the option parsing */
|
||||
log_set_prefix ("gpg-agent", 1|4);
|
||||
i18n_init ();
|
||||
|
||||
/* check that the libraries are suitable. Do it here because
|
||||
the option parsing may need services of the library */
|
||||
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
|
||||
{
|
||||
log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
|
||||
NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
|
||||
}
|
||||
|
||||
assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
|
||||
#ifdef USE_GNU_PTH
|
||||
assuan_set_io_func (pth_read, pth_write);
|
||||
#endif
|
||||
|
||||
gcry_set_log_handler (my_gcry_logger, NULL);
|
||||
gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
|
||||
|
||||
may_coredump = disable_core_dumps ();
|
||||
|
||||
shell = getenv ("SHELL");
|
||||
if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") )
|
||||
csh_style = 1;
|
||||
|
||||
opt.homedir = getenv("GNUPGHOME");
|
||||
if (!opt.homedir || !*opt.homedir)
|
||||
opt.homedir = GNUPG_DEFAULT_HOMEDIR;
|
||||
opt.def_cache_ttl = 10*60; /* default to 10 minutes */
|
||||
|
||||
|
||||
/* check whether we have a config file on the commandline */
|
||||
orig_argc = argc;
|
||||
orig_argv = argv;
|
||||
pargs.argc = &argc;
|
||||
pargs.argv = &argv;
|
||||
pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */
|
||||
while (arg_parse( &pargs, opts))
|
||||
{
|
||||
if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll)
|
||||
parse_debug++;
|
||||
else if (pargs.r_opt == oOptions)
|
||||
{ /* yes there is one, so we do not try the default one, but
|
||||
read the option file when it is encountered at the
|
||||
commandline */
|
||||
default_config = 0;
|
||||
}
|
||||
else if (pargs.r_opt == oNoOptions)
|
||||
default_config = 0; /* --no-options */
|
||||
else if (pargs.r_opt == oHomedir)
|
||||
opt.homedir = pargs.r.ret_str;
|
||||
}
|
||||
|
||||
/* initialize the secure memory. */
|
||||
gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
|
||||
maybe_setuid = 0;
|
||||
|
||||
/*
|
||||
Now we are now working under our real uid
|
||||
*/
|
||||
|
||||
|
||||
if (default_config)
|
||||
configname = make_filename (opt.homedir, "gpg-agent.conf", NULL );
|
||||
|
||||
argc = orig_argc;
|
||||
argv = orig_argv;
|
||||
pargs.argc = &argc;
|
||||
pargs.argv = &argv;
|
||||
pargs.flags= 1; /* do not remove the args */
|
||||
next_pass:
|
||||
if (configname)
|
||||
{
|
||||
configlineno = 0;
|
||||
configfp = fopen (configname, "r");
|
||||
if (!configfp)
|
||||
{
|
||||
if (default_config)
|
||||
{
|
||||
if( parse_debug )
|
||||
log_info (_("NOTE: no default option file `%s'\n"),
|
||||
configname );
|
||||
}
|
||||
else
|
||||
{
|
||||
log_error (_("option file `%s': %s\n"),
|
||||
configname, strerror(errno) );
|
||||
exit(2);
|
||||
}
|
||||
xfree (configname);
|
||||
configname = NULL;
|
||||
}
|
||||
if (parse_debug && configname )
|
||||
log_info (_("reading options from `%s'\n"), configname );
|
||||
default_config = 0;
|
||||
}
|
||||
|
||||
while (optfile_parse( configfp, configname, &configlineno, &pargs, opts) )
|
||||
{
|
||||
switch (pargs.r_opt)
|
||||
{
|
||||
case oQuiet: opt.quiet = 1; break;
|
||||
case oVerbose: opt.verbose++; break;
|
||||
case oBatch: opt.batch=1; break;
|
||||
|
||||
case oDebug: opt.debug |= pargs.r.ret_ulong; break;
|
||||
case oDebugAll: opt.debug = ~0; break;
|
||||
case oDebugWait: debug_wait = pargs.r.ret_int; break;
|
||||
|
||||
case oOptions:
|
||||
/* config files may not be nested (silently ignore them) */
|
||||
if (!configfp)
|
||||
{
|
||||
xfree(configname);
|
||||
configname = xstrdup(pargs.r.ret_str);
|
||||
goto next_pass;
|
||||
}
|
||||
break;
|
||||
case oNoGreeting: nogreeting = 1; break;
|
||||
case oNoVerbose: opt.verbose = 0; break;
|
||||
case oNoOptions: break; /* no-options */
|
||||
case oHomedir: opt.homedir = pargs.r.ret_str; break;
|
||||
case oNoDetach: nodetach = 1; break;
|
||||
case oNoGrab: opt.no_grab = 1; break;
|
||||
case oLogFile: logfile = pargs.r.ret_str; break;
|
||||
case oCsh: csh_style = 1; break;
|
||||
case oSh: csh_style = 0; break;
|
||||
case oServer: pipe_server = 1; break;
|
||||
case oDaemon: is_daemon = 1; break;
|
||||
case oDisablePth: disable_pth = 1; break;
|
||||
|
||||
case oPinentryProgram: opt.pinentry_program = pargs.r.ret_str; break;
|
||||
case oDisplay: opt.display = xstrdup (pargs.r.ret_str); break;
|
||||
case oTTYname: opt.ttyname = xstrdup (pargs.r.ret_str); break;
|
||||
case oTTYtype: opt.ttytype = xstrdup (pargs.r.ret_str); break;
|
||||
case oLCctype: opt.lc_ctype = xstrdup (pargs.r.ret_str); break;
|
||||
case oLCmessages: opt.lc_messages = xstrdup (pargs.r.ret_str); break;
|
||||
case oScdaemonProgram: opt.scdaemon_program = pargs.r.ret_str; break;
|
||||
case oDefCacheTTL: opt.def_cache_ttl = pargs.r.ret_ulong; break;
|
||||
|
||||
case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break;
|
||||
case oKeepTTY: opt.keep_tty = 1; break;
|
||||
case oKeepDISPLAY: opt.keep_display = 1; break;
|
||||
|
||||
default : pargs.err = configfp? 1:2; break;
|
||||
}
|
||||
}
|
||||
if (configfp)
|
||||
{
|
||||
fclose( configfp );
|
||||
configfp = NULL;
|
||||
xfree(configname);
|
||||
configname = NULL;
|
||||
goto next_pass;
|
||||
}
|
||||
xfree (configname);
|
||||
configname = NULL;
|
||||
if (log_get_errorcount(0))
|
||||
exit(2);
|
||||
if (nogreeting )
|
||||
greeting = 0;
|
||||
|
||||
if (greeting)
|
||||
{
|
||||
fprintf (stderr, "%s %s; %s\n",
|
||||
strusage(11), strusage(13), strusage(14) );
|
||||
fprintf (stderr, "%s\n", strusage(15) );
|
||||
}
|
||||
#ifdef IS_DEVELOPMENT_VERSION
|
||||
log_info ("NOTE: this is a development version!\n");
|
||||
#endif
|
||||
|
||||
|
||||
if (atexit (cleanup))
|
||||
{
|
||||
log_error ("atexit failed\n");
|
||||
cleanup ();
|
||||
exit (1);
|
||||
}
|
||||
|
||||
create_directories ();
|
||||
|
||||
if (debug_wait && pipe_server)
|
||||
{
|
||||
log_debug ("waiting for debugger - my pid is %u .....\n",
|
||||
(unsigned int)getpid());
|
||||
sleep (debug_wait);
|
||||
log_debug ("... okay\n");
|
||||
}
|
||||
|
||||
/* now start with logging to a file if this is desired */
|
||||
if (logfile)
|
||||
{
|
||||
log_set_file (logfile);
|
||||
log_set_prefix (NULL, 1|2|4);
|
||||
}
|
||||
|
||||
|
||||
if (pipe_server)
|
||||
{ /* this is the simple pipe based server */
|
||||
start_command_handler (-1, -1);
|
||||
}
|
||||
else if (!is_daemon)
|
||||
{
|
||||
log_info (_("please use the option `--daemon'"
|
||||
" to run the program in the background\n"));
|
||||
}
|
||||
else
|
||||
{ /* regular server mode */
|
||||
int fd;
|
||||
pid_t pid;
|
||||
int len;
|
||||
struct sockaddr_un serv_addr;
|
||||
char *p;
|
||||
|
||||
*socket_name = 0;
|
||||
snprintf (socket_name, DIM(socket_name)-1,
|
||||
"/tmp/gpg-XXXXXX/S.gpg-agent");
|
||||
socket_name[DIM(socket_name)-1] = 0;
|
||||
p = strrchr (socket_name, '/');
|
||||
if (!p)
|
||||
BUG ();
|
||||
*p = 0;;
|
||||
if (!mkdtemp(socket_name))
|
||||
{
|
||||
log_error ("can't create directory `%s': %s\n",
|
||||
socket_name, strerror(errno) );
|
||||
exit (1);
|
||||
}
|
||||
*p = '/';
|
||||
|
||||
if (strchr (socket_name, ':') )
|
||||
{
|
||||
log_error ("colons are not allowed in the socket name\n");
|
||||
exit (1);
|
||||
}
|
||||
if (strlen (socket_name)+1 >= sizeof serv_addr.sun_path )
|
||||
{
|
||||
log_error ("name of socket too long\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
|
||||
fd = socket (AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
{
|
||||
log_error ("can't create socket: %s\n", strerror(errno) );
|
||||
exit (1);
|
||||
}
|
||||
|
||||
memset (&serv_addr, 0, sizeof serv_addr);
|
||||
serv_addr.sun_family = AF_UNIX;
|
||||
strcpy (serv_addr.sun_path, socket_name);
|
||||
len = (offsetof (struct sockaddr_un, sun_path)
|
||||
+ strlen(serv_addr.sun_path) + 1);
|
||||
|
||||
if (bind (fd, (struct sockaddr*)&serv_addr, len) == -1)
|
||||
{
|
||||
log_error ("error binding socket to `%s': %s\n",
|
||||
serv_addr.sun_path, strerror (errno) );
|
||||
close (fd);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (listen (fd, 5 ) == -1)
|
||||
{
|
||||
log_error ("listen() failed: %s\n", strerror (errno));
|
||||
close (fd);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (opt.verbose)
|
||||
log_info ("listening on socket `%s'\n", socket_name );
|
||||
|
||||
|
||||
fflush (NULL);
|
||||
pid = fork ();
|
||||
if (pid == (pid_t)-1)
|
||||
{
|
||||
log_fatal ("fork failed: %s\n", strerror (errno) );
|
||||
exit (1);
|
||||
}
|
||||
else if (pid)
|
||||
{ /* we are the parent */
|
||||
char *infostr;
|
||||
|
||||
close (fd);
|
||||
|
||||
/* create the info string: <name>:<pid>:<protocol_version> */
|
||||
if (asprintf (&infostr, "GPG_AGENT_INFO=%s:%lu:1",
|
||||
socket_name, (ulong)pid ) < 0)
|
||||
{
|
||||
log_error ("out of core\n");
|
||||
kill (pid, SIGTERM);
|
||||
exit (1);
|
||||
}
|
||||
*socket_name = 0; /* don't let cleanup() remove the socket -
|
||||
the child should do this from now on */
|
||||
if (argc)
|
||||
{ /* run the program given on the commandline */
|
||||
if (putenv (infostr))
|
||||
{
|
||||
log_error ("failed to set environment: %s\n",
|
||||
strerror (errno) );
|
||||
kill (pid, SIGTERM );
|
||||
exit (1);
|
||||
}
|
||||
execvp (argv[0], argv);
|
||||
log_error ("failed to run the command: %s\n", strerror (errno));
|
||||
kill (pid, SIGTERM);
|
||||
exit (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* print the environment string, so that the caller can use
|
||||
shell's eval to set it */
|
||||
if (csh_style)
|
||||
{
|
||||
*strchr (infostr, '=') = ' ';
|
||||
printf ( "setenv %s\n", infostr);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ( "%s; export GPG_AGENT_INFO;\n", infostr);
|
||||
}
|
||||
free (infostr);
|
||||
exit (0);
|
||||
}
|
||||
/*NEVER REACHED*/
|
||||
} /* end parent */
|
||||
|
||||
|
||||
/* this is the child */
|
||||
|
||||
/* detach from tty and put process into a new session */
|
||||
if (!nodetach )
|
||||
{
|
||||
int i;
|
||||
|
||||
/* close stdin, stdout and stderr unless it is the log stream */
|
||||
for (i=0; i <= 2; i++)
|
||||
{
|
||||
if ( log_get_fd () != i)
|
||||
close (i);
|
||||
}
|
||||
if (setsid() == -1)
|
||||
{
|
||||
log_error ("setsid() failed: %s\n", strerror(errno) );
|
||||
cleanup ();
|
||||
exit (1);
|
||||
}
|
||||
opt.running_detached = 1;
|
||||
}
|
||||
|
||||
if (chdir("/"))
|
||||
{
|
||||
log_error ("chdir to / failed: %s\n", strerror (errno));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_GNU_PTH
|
||||
if (!disable_pth)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
if (!pth_init ())
|
||||
{
|
||||
log_error ("failed to initialize the Pth library\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigemptyset (&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sigaction (SIGPIPE, &sa, NULL);
|
||||
handle_connections (fd);
|
||||
}
|
||||
else
|
||||
#endif /*!USE_GNU_PTH*/
|
||||
/* setup signals */
|
||||
{
|
||||
struct sigaction oact, nact;
|
||||
|
||||
nact.sa_handler = cleanup_sh;
|
||||
sigemptyset (&nact.sa_mask);
|
||||
nact.sa_flags = 0;
|
||||
|
||||
sigaction (SIGHUP, NULL, &oact);
|
||||
if (oact.sa_handler != SIG_IGN)
|
||||
sigaction (SIGHUP, &nact, NULL);
|
||||
sigaction( SIGTERM, NULL, &oact );
|
||||
if (oact.sa_handler != SIG_IGN)
|
||||
sigaction (SIGTERM, &nact, NULL);
|
||||
nact.sa_handler = SIG_IGN;
|
||||
sigaction (SIGPIPE, &nact, NULL);
|
||||
sigaction (SIGINT, &nact, NULL);
|
||||
|
||||
start_command_handler (fd, -1);
|
||||
}
|
||||
close (fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
agent_exit (int rc)
|
||||
{
|
||||
/*FIXME: update_random_seed_file();*/
|
||||
#if 1
|
||||
/* at this time a bit annoying */
|
||||
if (opt.debug & DBG_MEMSTAT_VALUE)
|
||||
{
|
||||
gcry_control( GCRYCTL_DUMP_MEMORY_STATS );
|
||||
gcry_control( GCRYCTL_DUMP_RANDOM_STATS );
|
||||
}
|
||||
if (opt.debug)
|
||||
gcry_control (GCRYCTL_DUMP_SECMEM_STATS );
|
||||
#endif
|
||||
gcry_control (GCRYCTL_TERM_SECMEM );
|
||||
rc = rc? rc : log_get_errorcount(0)? 2 : 0;
|
||||
exit (rc);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
reread_configuration (void)
|
||||
{
|
||||
/* FIXME: Move parts of the option parsing to here. */
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
create_private_keys_directory (const char *home)
|
||||
{
|
||||
char *fname;
|
||||
struct stat statbuf;
|
||||
|
||||
fname = make_filename (home, GNUPG_PRIVATE_KEYS_DIR, NULL);
|
||||
if (stat (fname, &statbuf) && errno == ENOENT)
|
||||
{
|
||||
if (mkdir (fname, S_IRUSR|S_IWUSR|S_IXUSR ))
|
||||
log_error (_("can't create directory `%s': %s\n"),
|
||||
fname, strerror(errno) );
|
||||
else if (!opt.quiet)
|
||||
log_info (_("directory `%s' created\n"), fname);
|
||||
}
|
||||
xfree (fname);
|
||||
}
|
||||
|
||||
/* Create the directory only if the supplied directory name is the
|
||||
same as the default one. This way we avoid to create arbitrary
|
||||
directories when a non-default home directory is used. To cope
|
||||
with HOME, we compare only the suffix if we see that the default
|
||||
homedir does start with a tilde. We don't stop here in case of
|
||||
problems because other functions will throw an error anyway.*/
|
||||
static void
|
||||
create_directories (void)
|
||||
{
|
||||
struct stat statbuf;
|
||||
const char *defhome = GNUPG_DEFAULT_HOMEDIR;
|
||||
char *home;
|
||||
|
||||
home = make_filename (opt.homedir, NULL);
|
||||
if ( stat (home, &statbuf) )
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
if ( (*defhome == '~'
|
||||
&& (strlen (home) >= strlen (defhome+1)
|
||||
&& !strcmp (home + strlen(home)
|
||||
- strlen (defhome+1), defhome+1)))
|
||||
|| (*defhome != '~' && !strcmp (home, defhome) )
|
||||
)
|
||||
{
|
||||
if (mkdir (home, S_IRUSR|S_IWUSR|S_IXUSR ))
|
||||
log_error (_("can't create directory `%s': %s\n"),
|
||||
home, strerror(errno) );
|
||||
else
|
||||
{
|
||||
if (!opt.quiet)
|
||||
log_info (_("directory `%s' created\n"), home);
|
||||
create_private_keys_directory (home);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
log_error ("error stat-ing `%s': %s\n", home, strerror (errno));
|
||||
}
|
||||
else if ( !S_ISDIR(statbuf.st_mode))
|
||||
{
|
||||
log_error ("can't use `%s' as home directory\n", home);
|
||||
}
|
||||
else /* exists and is a directory. */
|
||||
{
|
||||
create_private_keys_directory (home);
|
||||
}
|
||||
xfree (home);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef USE_GNU_PTH
|
||||
static void
|
||||
handle_signal (int signo)
|
||||
{
|
||||
switch (signo)
|
||||
{
|
||||
case SIGHUP:
|
||||
log_info ("SIGHUP received - "
|
||||
"re-reading configuration and flushing cache\n");
|
||||
agent_flush_cache ();
|
||||
reread_configuration ();
|
||||
break;
|
||||
|
||||
case SIGUSR1:
|
||||
if (opt.verbose < 5)
|
||||
opt.verbose++;
|
||||
log_info ("SIGUSR1 received - verbosity set to %d\n", opt.verbose);
|
||||
break;
|
||||
|
||||
case SIGUSR2:
|
||||
if (opt.verbose)
|
||||
opt.verbose--;
|
||||
log_info ("SIGUSR2 received - verbosity set to %d\n", opt.verbose );
|
||||
break;
|
||||
|
||||
case SIGTERM:
|
||||
if (!shutdown_pending)
|
||||
log_info ("SIGTERM received - shutting down ...\n");
|
||||
else
|
||||
log_info ("SIGTERM received - still %ld running threads\n",
|
||||
pth_ctrl( PTH_CTRL_GETTHREADS ));
|
||||
shutdown_pending++;
|
||||
if (shutdown_pending > 2)
|
||||
{
|
||||
log_info ("shutdown forced\n");
|
||||
log_info ("%s %s stopped\n", strusage(11), strusage(13) );
|
||||
cleanup ();
|
||||
agent_exit (0);
|
||||
}
|
||||
break;
|
||||
|
||||
case SIGINT:
|
||||
log_info ("SIGINT received - immediate shutdown\n");
|
||||
log_info( "%s %s stopped\n", strusage(11), strusage(13));
|
||||
cleanup ();
|
||||
agent_exit (0);
|
||||
break;
|
||||
|
||||
default:
|
||||
log_info ("signal %d received - no action defined\n", signo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
start_connection_thread (void *arg)
|
||||
{
|
||||
int fd = (int)arg;
|
||||
|
||||
if (opt.verbose)
|
||||
log_info ("handler for fd %d started\n", fd);
|
||||
start_command_handler (-1, fd);
|
||||
if (opt.verbose)
|
||||
log_info ("handler for fd %d terminated\n", fd);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
handle_connections (int listen_fd)
|
||||
{
|
||||
pth_attr_t tattr;
|
||||
pth_event_t ev;
|
||||
sigset_t sigs;
|
||||
int signo;
|
||||
struct sockaddr_un paddr;
|
||||
socklen_t plen = sizeof( paddr );
|
||||
int fd;
|
||||
|
||||
tattr = pth_attr_new();
|
||||
pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
|
||||
pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 32*1024);
|
||||
pth_attr_set (tattr, PTH_ATTR_NAME, "gpg-agent");
|
||||
|
||||
sigemptyset (&sigs );
|
||||
sigaddset (&sigs, SIGHUP);
|
||||
sigaddset (&sigs, SIGUSR1);
|
||||
sigaddset (&sigs, SIGUSR2);
|
||||
sigaddset (&sigs, SIGINT);
|
||||
sigaddset (&sigs, SIGTERM);
|
||||
ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (shutdown_pending)
|
||||
{
|
||||
if (pth_ctrl (PTH_CTRL_GETTHREADS) == 1)
|
||||
break; /* ready */
|
||||
|
||||
/* Do not accept anymore connections and wait for existing
|
||||
connections to terminate */
|
||||
signo = 0;
|
||||
pth_wait (ev);
|
||||
if (pth_event_occurred (ev) && signo)
|
||||
handle_signal (signo);
|
||||
continue;
|
||||
}
|
||||
|
||||
fd = pth_accept_ev (listen_fd, (struct sockaddr *)&paddr, &plen, ev);
|
||||
if (fd == -1)
|
||||
{
|
||||
if (pth_event_occurred (ev))
|
||||
{
|
||||
handle_signal (signo);
|
||||
continue;
|
||||
}
|
||||
log_error ("accept failed: %s - waiting 1s\n", strerror (errno));
|
||||
pth_sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pth_spawn (tattr, start_connection_thread, (void*)fd))
|
||||
{
|
||||
log_error ("error spawning connection handler: %s\n",
|
||||
strerror (errno) );
|
||||
close (fd);
|
||||
}
|
||||
}
|
||||
|
||||
pth_event_free (ev, PTH_FREE_ALL);
|
||||
cleanup ();
|
||||
log_info ("%s %s stopped\n", strusage(11), strusage(13));
|
||||
}
|
||||
#endif /*USE_GNU_PTH*/
|
|
@ -1,380 +0,0 @@
|
|||
/* learncard.c - Handle the LEARN command
|
||||
* Copyright (C) 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "agent.h"
|
||||
#include "../assuan/assuan.h"
|
||||
|
||||
struct keypair_info_s {
|
||||
struct keypair_info_s *next;
|
||||
int no_cert;
|
||||
char *id; /* points into grip */
|
||||
char hexgrip[1];
|
||||
};
|
||||
typedef struct keypair_info_s *KEYPAIR_INFO;
|
||||
|
||||
struct kpinfo_cb_parm_s {
|
||||
int error;
|
||||
KEYPAIR_INFO info;
|
||||
};
|
||||
|
||||
|
||||
struct certinfo_s {
|
||||
struct certinfo_s *next;
|
||||
int type;
|
||||
int done;
|
||||
char id[1];
|
||||
};
|
||||
typedef struct certinfo_s *CERTINFO;
|
||||
|
||||
struct certinfo_cb_parm_s {
|
||||
int error;
|
||||
CERTINFO info;
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
release_keypair_info (KEYPAIR_INFO info)
|
||||
{
|
||||
while (info)
|
||||
{
|
||||
KEYPAIR_INFO tmp = info->next;
|
||||
xfree (info);
|
||||
info = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
release_certinfo (CERTINFO info)
|
||||
{
|
||||
while (info)
|
||||
{
|
||||
CERTINFO tmp = info->next;
|
||||
xfree (info);
|
||||
info = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This callback is used by agent_card_leanr and passed the content of
|
||||
all KEYPAIRINFO lines. It merely stores this data away */
|
||||
static void
|
||||
kpinfo_cb (void *opaque, const char *line)
|
||||
{
|
||||
struct kpinfo_cb_parm_s *parm = opaque;
|
||||
KEYPAIR_INFO item;
|
||||
char *p;
|
||||
|
||||
if (parm->error)
|
||||
return; /* no need to gather data after an error coccured */
|
||||
item = xtrycalloc (1, sizeof *item + strlen (line));
|
||||
if (!item)
|
||||
{
|
||||
parm->error = GNUPG_Out_Of_Core;
|
||||
return;
|
||||
}
|
||||
strcpy (item->hexgrip, line);
|
||||
for (p = item->hexgrip; hexdigitp (p); p++)
|
||||
;
|
||||
if (p == item->hexgrip && *p == 'X' && spacep (p+1))
|
||||
{
|
||||
item->no_cert = 1;
|
||||
p++;
|
||||
}
|
||||
else if ((p - item->hexgrip) != 40 || !spacep (p))
|
||||
{ /* not a 20 byte hex keygrip or not followed by a space */
|
||||
parm->error = GNUPG_Invalid_Response;
|
||||
xfree (item);
|
||||
return;
|
||||
}
|
||||
*p++ = 0;
|
||||
while (spacep (p))
|
||||
p++;
|
||||
item->id = p;
|
||||
while (*p && !spacep (p))
|
||||
p++;
|
||||
if (p == item->id)
|
||||
{ /* invalid ID string */
|
||||
parm->error = GNUPG_Invalid_Response;
|
||||
xfree (item);
|
||||
return;
|
||||
}
|
||||
*p = 0; /* ignore trailing stuff */
|
||||
|
||||
/* store it */
|
||||
item->next = parm->info;
|
||||
parm->info = item;
|
||||
}
|
||||
|
||||
|
||||
/* This callback is used by agent_card_leanr and passed the content of
|
||||
all CERTINFO lines. It merely stores this data away */
|
||||
static void
|
||||
certinfo_cb (void *opaque, const char *line)
|
||||
{
|
||||
struct certinfo_cb_parm_s *parm = opaque;
|
||||
CERTINFO item;
|
||||
int type;
|
||||
char *p, *pend;
|
||||
|
||||
if (parm->error)
|
||||
return; /* no need to gather data after an error coccured */
|
||||
|
||||
type = strtol (line, &p, 10);
|
||||
while (spacep (p))
|
||||
p++;
|
||||
for (pend = p; *pend && !spacep (pend); pend++)
|
||||
;
|
||||
if (p == pend || !*p)
|
||||
{
|
||||
parm->error = GNUPG_Invalid_Response;
|
||||
return;
|
||||
}
|
||||
*pend = 0; /* ignore trailing stuff */
|
||||
|
||||
item = xtrycalloc (1, sizeof *item + strlen (p));
|
||||
if (!item)
|
||||
{
|
||||
parm->error = GNUPG_Out_Of_Core;
|
||||
return;
|
||||
}
|
||||
item->type = type;
|
||||
strcpy (item->id, p);
|
||||
/* store it */
|
||||
item->next = parm->info;
|
||||
parm->info = item;
|
||||
}
|
||||
|
||||
|
||||
/* Create an S-expression with the shadow info. */
|
||||
static unsigned char *
|
||||
make_shadow_info (const char *serialno, const char *idstring)
|
||||
{
|
||||
const char *s;
|
||||
unsigned char *info, *p;
|
||||
char numbuf[21];
|
||||
int n;
|
||||
|
||||
for (s=serialno, n=0; *s && s[1]; s += 2)
|
||||
n++;
|
||||
|
||||
info = p = xtrymalloc (1 + 21 + n
|
||||
+ 21 + strlen (idstring) + 1 + 1);
|
||||
*p++ = '(';
|
||||
sprintf (numbuf, "%d:", n);
|
||||
p = stpcpy (p, numbuf);
|
||||
for (s=serialno; *s && s[1]; s += 2)
|
||||
*p++ = xtoi_2 (s);
|
||||
sprintf (numbuf, "%d:", strlen (idstring));
|
||||
p = stpcpy (p, numbuf);
|
||||
p = stpcpy (p, idstring);
|
||||
*p++ = ')';
|
||||
*p = 0;
|
||||
return info;
|
||||
}
|
||||
|
||||
static int
|
||||
send_cert_back (const char *id, void *assuan_context)
|
||||
{
|
||||
int rc;
|
||||
char *derbuf;
|
||||
size_t derbuflen;
|
||||
|
||||
rc = agent_card_readcert (id, &derbuf, &derbuflen);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("error reading certificate: %s\n",
|
||||
gnupg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = assuan_send_data (assuan_context, derbuf, derbuflen);
|
||||
xfree (derbuf);
|
||||
if (!rc)
|
||||
rc = assuan_send_data (assuan_context, NULL, 0);
|
||||
if (!rc)
|
||||
rc = assuan_write_line (assuan_context, "END");
|
||||
if (rc)
|
||||
{
|
||||
log_error ("sending certificate failed: %s\n",
|
||||
assuan_strerror (rc));
|
||||
return map_assuan_err (rc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Perform the learn operation. If ASSUAN_CONTEXT is not NULL all new
|
||||
certificates are send via Assuan */
|
||||
int
|
||||
agent_handle_learn (void *assuan_context)
|
||||
{
|
||||
int rc;
|
||||
struct kpinfo_cb_parm_s parm;
|
||||
struct certinfo_cb_parm_s cparm;
|
||||
char *serialno = NULL;
|
||||
KEYPAIR_INFO item;
|
||||
unsigned char grip[20];
|
||||
char *p;
|
||||
int i;
|
||||
static int certtype_list[] = {
|
||||
101, /* trusted */
|
||||
102, /* useful */
|
||||
100, /* regular */
|
||||
-1 /* end of list */
|
||||
};
|
||||
|
||||
|
||||
memset (&parm, 0, sizeof parm);
|
||||
memset (&cparm, 0, sizeof cparm);
|
||||
|
||||
/* Check whether a card is present and get the serial number */
|
||||
rc = agent_card_serialno (&serialno);
|
||||
if (rc)
|
||||
goto leave;
|
||||
|
||||
/* now gather all the availabe info */
|
||||
rc = agent_card_learn (kpinfo_cb, &parm, certinfo_cb, &cparm);
|
||||
if (!rc && (parm.error || cparm.error))
|
||||
rc = parm.error? parm.error : cparm.error;
|
||||
if (rc)
|
||||
{
|
||||
log_debug ("agent_card_learn failed: %s\n", gnupg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
log_info ("card has S/N: %s\n", serialno);
|
||||
|
||||
/* Write out the certificates in a standard order. */
|
||||
for (i=0; certtype_list[i] != -1; i++)
|
||||
{
|
||||
CERTINFO citem;
|
||||
for (citem = cparm.info; citem; citem = citem->next)
|
||||
{
|
||||
if (certtype_list[i] != citem->type)
|
||||
continue;
|
||||
|
||||
if (opt.verbose)
|
||||
log_info (" id: %s (type=%d)\n",
|
||||
citem->id, citem->type);
|
||||
|
||||
if (assuan_context)
|
||||
{
|
||||
rc = send_cert_back (citem->id, assuan_context);
|
||||
if (rc)
|
||||
goto leave;
|
||||
citem->done = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (item = parm.info; item; item = item->next)
|
||||
{
|
||||
unsigned char *pubkey, *shdkey;
|
||||
size_t n;
|
||||
|
||||
if (opt.verbose)
|
||||
log_info (" id: %s (grip=%s)\n", item->id, item->hexgrip);
|
||||
|
||||
if (item->no_cert)
|
||||
continue; /* no public key yet available */
|
||||
|
||||
for (p=item->hexgrip, i=0; i < 20; p += 2, i++)
|
||||
grip[i] = xtoi_2 (p);
|
||||
|
||||
if (!agent_key_available (grip))
|
||||
continue;
|
||||
|
||||
/* unknown - store it */
|
||||
rc = agent_card_readkey (item->id, &pubkey);
|
||||
if (rc)
|
||||
{
|
||||
log_debug ("agent_card_readkey failed: %s\n", gnupg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
{
|
||||
unsigned char *shadow_info = make_shadow_info (serialno, item->id);
|
||||
if (!shadow_info)
|
||||
{
|
||||
rc = GNUPG_Out_Of_Core;
|
||||
xfree (pubkey);
|
||||
goto leave;
|
||||
}
|
||||
rc = agent_shadow_key (pubkey, shadow_info, &shdkey);
|
||||
xfree (shadow_info);
|
||||
}
|
||||
xfree (pubkey);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("shadowing the key failed: %s\n", gnupg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
n = gcry_sexp_canon_len (shdkey, 0, NULL, NULL);
|
||||
assert (n);
|
||||
|
||||
rc = agent_write_private_key (grip, shdkey, n, 0);
|
||||
xfree (shdkey);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("error writing key: %s\n", gnupg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (opt.verbose)
|
||||
log_info ("stored\n");
|
||||
|
||||
if (assuan_context)
|
||||
{
|
||||
CERTINFO citem;
|
||||
|
||||
/* only send the certificate if we have not done so before */
|
||||
for (citem = cparm.info; citem; citem = citem->next)
|
||||
{
|
||||
if (!strcmp (citem->id, item->id))
|
||||
break;
|
||||
}
|
||||
if (!citem)
|
||||
{
|
||||
rc = send_cert_back (item->id, assuan_context);
|
||||
if (rc)
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
leave:
|
||||
xfree (serialno);
|
||||
release_keypair_info (parm.info);
|
||||
release_certinfo (cparm.info);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
1146
agent/minip12.c
1146
agent/minip12.c
File diff suppressed because it is too large
Load diff
|
@ -1,32 +0,0 @@
|
|||
/* minip12.h - Global definitions for the minimal pkcs-12 implementation.
|
||||
* Copyright (C) 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#ifndef MINIP12_H
|
||||
#define MINIP12_H
|
||||
|
||||
#include <gcrypt.h>
|
||||
|
||||
GcryMPI *p12_parse (const unsigned char *buffer, size_t length,
|
||||
const char *pw);
|
||||
|
||||
unsigned char *p12_build (GcryMPI *kparms, const char *pw, size_t *r_length);
|
||||
|
||||
|
||||
#endif /*MINIP12_H*/
|
|
@ -1,138 +0,0 @@
|
|||
/* pkdecrypt.c - public key decryption (well, acually using a secret key)
|
||||
* Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "agent.h"
|
||||
|
||||
|
||||
/* DECRYPT the stuff in ciphertext which is expected to be a S-Exp.
|
||||
Try to get the key from CTRL and write the decoded stuff back to
|
||||
OUTFP. */
|
||||
int
|
||||
agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen,
|
||||
FILE *outfp)
|
||||
{
|
||||
GCRY_SEXP s_skey = NULL, s_cipher = NULL, s_plain = NULL;
|
||||
unsigned char *shadow_info = NULL;
|
||||
int rc;
|
||||
char *buf = NULL;
|
||||
size_t len;
|
||||
|
||||
if (!ctrl->have_keygrip)
|
||||
{
|
||||
log_error ("speculative decryption not yet supported\n");
|
||||
rc = seterr (No_Secret_Key);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
rc = gcry_sexp_sscan (&s_cipher, NULL, ciphertext, ciphertextlen);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to convert ciphertext: %s\n", gcry_strerror (rc));
|
||||
rc = seterr (Invalid_Data);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (DBG_CRYPTO)
|
||||
{
|
||||
log_printhex ("keygrip:", ctrl->keygrip, 20);
|
||||
log_printhex ("cipher: ", ciphertext, ciphertextlen);
|
||||
}
|
||||
s_skey = agent_key_from_file (ctrl->keygrip, &shadow_info, 0);
|
||||
if (!s_skey && !shadow_info)
|
||||
{
|
||||
log_error ("failed to read the secret key\n");
|
||||
rc = seterr (No_Secret_Key);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (!s_skey)
|
||||
{ /* divert operation to the smartcard */
|
||||
|
||||
if (!gcry_sexp_canon_len (ciphertext, ciphertextlen, NULL, NULL))
|
||||
{
|
||||
rc = GNUPG_Invalid_Sexp;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
rc = divert_pkdecrypt (ciphertext, shadow_info, &buf, &len );
|
||||
if (rc)
|
||||
{
|
||||
log_error ("smartcard decryption failed: %s\n", gnupg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
/* FIXME: don't use buffering and change the protocol to return
|
||||
a complete S-expression and not just a part. */
|
||||
fprintf (outfp, "%u:", (unsigned int)len);
|
||||
fwrite (buf, 1, len, outfp);
|
||||
putc (0, outfp);
|
||||
}
|
||||
else
|
||||
{ /* no smartcard, but a private key */
|
||||
if (DBG_CRYPTO)
|
||||
{
|
||||
log_debug ("skey: ");
|
||||
gcry_sexp_dump (s_skey);
|
||||
}
|
||||
|
||||
rc = gcry_pk_decrypt (&s_plain, s_cipher, s_skey);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("decryption failed: %s\n", gcry_strerror (rc));
|
||||
rc = map_gcry_err (rc);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (DBG_CRYPTO)
|
||||
{
|
||||
log_debug ("plain: ");
|
||||
gcry_sexp_dump (s_plain);
|
||||
}
|
||||
len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, NULL, 0);
|
||||
assert (len);
|
||||
buf = xmalloc (len);
|
||||
len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, buf, len);
|
||||
assert (len);
|
||||
/* FIXME: we must make sure that no buffering takes place or we are
|
||||
in full control of the buffer memory (easy to do) - should go
|
||||
into assuan. */
|
||||
fwrite (buf, 1, len, outfp);
|
||||
}
|
||||
|
||||
|
||||
leave:
|
||||
gcry_sexp_release (s_skey);
|
||||
gcry_sexp_release (s_plain);
|
||||
gcry_sexp_release (s_cipher);
|
||||
xfree (buf);
|
||||
xfree (shadow_info);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
183
agent/pksign.c
183
agent/pksign.c
|
@ -1,183 +0,0 @@
|
|||
/* pksign.c - public key signing (well, acually using a secret key)
|
||||
* Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "agent.h"
|
||||
|
||||
|
||||
static int
|
||||
do_encode_md (const unsigned char *digest, size_t digestlen, int algo,
|
||||
unsigned int nbits, GCRY_MPI *r_val)
|
||||
{
|
||||
int nframe = (nbits+7) / 8;
|
||||
byte *frame;
|
||||
int i, n;
|
||||
byte asn[100];
|
||||
size_t asnlen;
|
||||
|
||||
asnlen = DIM(asn);
|
||||
if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
|
||||
{
|
||||
log_error ("no object identifier for algo %d\n", algo);
|
||||
return GNUPG_Internal_Error;
|
||||
}
|
||||
|
||||
if (digestlen + asnlen + 4 > nframe )
|
||||
{
|
||||
log_error ("can't encode a %d bit MD into a %d bits frame\n",
|
||||
(int)(digestlen*8), (int)nbits);
|
||||
return GNUPG_Internal_Error;
|
||||
}
|
||||
|
||||
/* We encode the MD in this way:
|
||||
*
|
||||
* 0 1 PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes)
|
||||
*
|
||||
* PAD consists of FF bytes.
|
||||
*/
|
||||
frame = xtrymalloc (nframe);
|
||||
if (!frame)
|
||||
return GNUPG_Out_Of_Core;
|
||||
n = 0;
|
||||
frame[n++] = 0;
|
||||
frame[n++] = 1; /* block type */
|
||||
i = nframe - digestlen - asnlen -3 ;
|
||||
assert ( i > 1 );
|
||||
memset ( frame+n, 0xff, i ); n += i;
|
||||
frame[n++] = 0;
|
||||
memcpy ( frame+n, asn, asnlen ); n += asnlen;
|
||||
memcpy ( frame+n, digest, digestlen ); n += digestlen;
|
||||
assert ( n == nframe );
|
||||
if (DBG_CRYPTO)
|
||||
log_printhex ("encoded hash:", frame, nframe);
|
||||
|
||||
gcry_mpi_scan (r_val, GCRYMPI_FMT_USG, frame, &nframe);
|
||||
xfree (frame);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* SIGN whatever information we have accumulated in CTRL and write it
|
||||
back to OUTFP. */
|
||||
int
|
||||
agent_pksign (CTRL ctrl, FILE *outfp, int ignore_cache)
|
||||
{
|
||||
GCRY_SEXP s_skey = NULL, s_hash = NULL, s_sig = NULL;
|
||||
GCRY_MPI frame = NULL;
|
||||
unsigned char *shadow_info = NULL;
|
||||
int rc;
|
||||
char *buf = NULL;
|
||||
size_t len;
|
||||
|
||||
if (!ctrl->have_keygrip)
|
||||
return seterr (No_Secret_Key);
|
||||
|
||||
s_skey = agent_key_from_file (ctrl->keygrip, &shadow_info, ignore_cache);
|
||||
if (!s_skey && !shadow_info)
|
||||
{
|
||||
log_error ("failed to read the secret key\n");
|
||||
rc = seterr (No_Secret_Key);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (!s_skey)
|
||||
{ /* divert operation to the smartcard */
|
||||
unsigned char *sigbuf;
|
||||
|
||||
rc = divert_pksign (ctrl->digest.value,
|
||||
ctrl->digest.valuelen,
|
||||
ctrl->digest.algo,
|
||||
shadow_info, &sigbuf);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("smartcard signing failed: %s\n", gnupg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
len = gcry_sexp_canon_len (sigbuf, 0, NULL, NULL);
|
||||
assert (len);
|
||||
buf = sigbuf;
|
||||
}
|
||||
else
|
||||
{ /* no smartcard, but a private key */
|
||||
|
||||
/* put the hash into a sexp */
|
||||
rc = do_encode_md (ctrl->digest.value,
|
||||
ctrl->digest.valuelen,
|
||||
ctrl->digest.algo,
|
||||
gcry_pk_get_nbits (s_skey),
|
||||
&frame);
|
||||
if (rc)
|
||||
goto leave;
|
||||
if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) )
|
||||
BUG ();
|
||||
|
||||
if (DBG_CRYPTO)
|
||||
{
|
||||
log_debug ("skey: ");
|
||||
gcry_sexp_dump (s_skey);
|
||||
}
|
||||
|
||||
/* sign */
|
||||
rc = gcry_pk_sign (&s_sig, s_hash, s_skey);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("signing failed: %s\n", gcry_strerror (rc));
|
||||
rc = map_gcry_err (rc);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (DBG_CRYPTO)
|
||||
{
|
||||
log_debug ("result: ");
|
||||
gcry_sexp_dump (s_sig);
|
||||
}
|
||||
|
||||
len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, NULL, 0);
|
||||
assert (len);
|
||||
buf = xmalloc (len);
|
||||
len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, buf, len);
|
||||
assert (len);
|
||||
}
|
||||
|
||||
/* FIXME: we must make sure that no buffering takes place or we are
|
||||
in full control of the buffer memory (easy to do) - should go
|
||||
into assuan. */
|
||||
fwrite (buf, 1, len, outfp);
|
||||
|
||||
leave:
|
||||
gcry_sexp_release (s_skey);
|
||||
gcry_sexp_release (s_hash);
|
||||
gcry_sexp_release (s_sig);
|
||||
gcry_mpi_release (frame);
|
||||
xfree (buf);
|
||||
xfree (shadow_info);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
@ -1,977 +0,0 @@
|
|||
/* protect-tool.c - A tool to test the secret key protection
|
||||
* Copyright (C) 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <gcrypt.h>
|
||||
|
||||
#define JNLIB_NEED_LOG_LOGV
|
||||
#include "agent.h"
|
||||
#include "minip12.h"
|
||||
#include "simple-pwquery.h"
|
||||
#include "i18n.h"
|
||||
|
||||
enum cmd_and_opt_values
|
||||
{ aNull = 0,
|
||||
oVerbose = 'v',
|
||||
oArmor = 'a',
|
||||
oPassphrase = 'P',
|
||||
|
||||
oProtect = 'p',
|
||||
oUnprotect = 'u',
|
||||
|
||||
oNoVerbose = 500,
|
||||
oShadow,
|
||||
oShowShadowInfo,
|
||||
oShowKeygrip,
|
||||
|
||||
oP12Import,
|
||||
oP12Export,
|
||||
oStore,
|
||||
oForce,
|
||||
|
||||
aTest };
|
||||
|
||||
struct rsa_secret_key_s
|
||||
{
|
||||
MPI n; /* public modulus */
|
||||
MPI e; /* public exponent */
|
||||
MPI d; /* exponent */
|
||||
MPI p; /* prime p. */
|
||||
MPI q; /* prime q. */
|
||||
MPI u; /* inverse of p mod q. */
|
||||
};
|
||||
|
||||
|
||||
static int opt_armor;
|
||||
static int opt_store;
|
||||
static int opt_force;
|
||||
static const char *passphrase;
|
||||
|
||||
static const char *get_passphrase (void);
|
||||
static int store_private_key (const unsigned char *grip,
|
||||
const void *buffer, size_t length, int force);
|
||||
|
||||
|
||||
static ARGPARSE_OPTS opts[] = {
|
||||
|
||||
{ 301, NULL, 0, N_("@Options:\n ") },
|
||||
|
||||
{ oVerbose, "verbose", 0, "verbose" },
|
||||
{ oArmor, "armor", 0, "write output in advanced format" },
|
||||
{ oPassphrase, "passphrase", 2, "|STRING|use passphrase STRING" },
|
||||
{ oProtect, "protect", 256, "protect a private key"},
|
||||
{ oUnprotect, "unprotect", 256, "unprotect a private key"},
|
||||
{ oShadow, "shadow", 256, "create a shadow entry for a priblic key"},
|
||||
{ oShowShadowInfo, "show-shadow-info", 256, "return the shadow info"},
|
||||
{ oShowKeygrip, "show-keygrip", 256, "show the \"keygrip\""},
|
||||
|
||||
{ oP12Import, "p12-import", 256, "import a PKCS-12 encoded private key"},
|
||||
{ oP12Export, "p12-export", 256, "export a private key PKCS-12 encoded"},
|
||||
{ oStore, "store", 0, "store the created key in the appropriate place"},
|
||||
{ oForce, "force", 0, "force overwriting"},
|
||||
{0}
|
||||
};
|
||||
|
||||
static const char *
|
||||
my_strusage (int level)
|
||||
{
|
||||
const char *p;
|
||||
switch (level)
|
||||
{
|
||||
case 11: p = "gpg-protect-tool (GnuPG)";
|
||||
break;
|
||||
case 13: p = VERSION; break;
|
||||
case 17: p = PRINTABLE_OS_NAME; break;
|
||||
case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
|
||||
break;
|
||||
case 1:
|
||||
case 40: p = _("Usage: gpg-protect-tool [options] (-h for help)\n");
|
||||
break;
|
||||
case 41: p = _("Syntax: gpg-protect-tool [options] [args]]\n"
|
||||
"Secret key maintenance tool\n");
|
||||
break;
|
||||
|
||||
default: p = NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
i18n_init (void)
|
||||
{
|
||||
#ifdef USE_SIMPLE_GETTEXT
|
||||
set_gettext_file( PACKAGE );
|
||||
#else
|
||||
#ifdef ENABLE_NLS
|
||||
setlocale (LC_ALL, "");
|
||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
||||
textdomain (PACKAGE);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Used by gcry for logging */
|
||||
static void
|
||||
my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
|
||||
{
|
||||
/* translate the log levels */
|
||||
switch (level)
|
||||
{
|
||||
case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break;
|
||||
case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break;
|
||||
case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break;
|
||||
case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break;
|
||||
case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
|
||||
case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break;
|
||||
case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
|
||||
default: level = JNLIB_LOG_ERROR; break; }
|
||||
log_logv (level, fmt, arg_ptr);
|
||||
}
|
||||
|
||||
|
||||
/* static void */
|
||||
/* print_mpi (const char *text, GcryMPI a) */
|
||||
/* { */
|
||||
/* char *buf; */
|
||||
/* void *bufaddr = &buf; */
|
||||
/* int rc; */
|
||||
|
||||
/* rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, bufaddr, NULL, a); */
|
||||
/* if (rc) */
|
||||
/* log_info ("%s: [error printing number: %s]\n", text, gcry_strerror (rc)); */
|
||||
/* else */
|
||||
/* { */
|
||||
/* log_info ("%s: %s\n", text, buf); */
|
||||
/* gcry_free (buf); */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
|
||||
|
||||
static unsigned char *
|
||||
make_canonical (const char *fname, const char *buf, size_t buflen)
|
||||
{
|
||||
int rc;
|
||||
size_t erroff, len;
|
||||
GCRY_SEXP sexp;
|
||||
unsigned char *result;
|
||||
|
||||
rc = gcry_sexp_sscan (&sexp, &erroff, buf, buflen);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("invalid S-Expression in `%s' (off=%u): %s\n",
|
||||
fname, (unsigned int)erroff, gcry_strerror (rc));
|
||||
return NULL;
|
||||
}
|
||||
len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0);
|
||||
assert (len);
|
||||
result = xmalloc (len);
|
||||
len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, result, len);
|
||||
assert (len);
|
||||
gcry_sexp_release (sexp);
|
||||
return result;
|
||||
}
|
||||
|
||||
static char *
|
||||
make_advanced (const unsigned char *buf, size_t buflen)
|
||||
{
|
||||
int rc;
|
||||
size_t erroff, len;
|
||||
GCRY_SEXP sexp;
|
||||
unsigned char *result;
|
||||
|
||||
rc = gcry_sexp_sscan (&sexp, &erroff, buf, buflen);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("invalid canonical S-Expression (off=%u): %s\n",
|
||||
(unsigned int)erroff, gcry_strerror (rc));
|
||||
return NULL;
|
||||
}
|
||||
len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
|
||||
assert (len);
|
||||
result = xmalloc (len);
|
||||
len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
|
||||
assert (len);
|
||||
gcry_sexp_release (sexp);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
read_file (const char *fname, size_t *r_length)
|
||||
{
|
||||
FILE *fp;
|
||||
struct stat st;
|
||||
char *buf;
|
||||
size_t buflen;
|
||||
|
||||
fp = fopen (fname, "rb");
|
||||
if (!fp)
|
||||
{
|
||||
log_error ("can't open `%s': %s\n", fname, strerror (errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fstat (fileno(fp), &st))
|
||||
{
|
||||
log_error ("can't stat `%s': %s\n", fname, strerror (errno));
|
||||
fclose (fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buflen = st.st_size;
|
||||
buf = xmalloc (buflen+1);
|
||||
if (fread (buf, buflen, 1, fp) != 1)
|
||||
{
|
||||
log_error ("error reading `%s': %s\n", fname, strerror (errno));
|
||||
fclose (fp);
|
||||
xfree (buf);
|
||||
return NULL;
|
||||
}
|
||||
fclose (fp);
|
||||
|
||||
*r_length = buflen;
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
static unsigned char *
|
||||
read_key (const char *fname)
|
||||
{
|
||||
char *buf;
|
||||
size_t buflen;
|
||||
unsigned char *key;
|
||||
|
||||
buf = read_file (fname, &buflen);
|
||||
key = make_canonical (fname, buf, buflen);
|
||||
xfree (buf);
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
read_and_protect (const char *fname)
|
||||
{
|
||||
int rc;
|
||||
unsigned char *key;
|
||||
unsigned char *result;
|
||||
size_t resultlen;
|
||||
|
||||
key = read_key (fname);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
rc = agent_protect (key, get_passphrase (), &result, &resultlen);
|
||||
xfree (key);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("protecting the key failed: %s\n", gnupg_strerror (rc));
|
||||
return;
|
||||
}
|
||||
|
||||
if (opt_armor)
|
||||
{
|
||||
char *p = make_advanced (result, resultlen);
|
||||
xfree (result);
|
||||
if (!p)
|
||||
return;
|
||||
result = p;
|
||||
resultlen = strlen (p);
|
||||
}
|
||||
|
||||
fwrite (result, resultlen, 1, stdout);
|
||||
xfree (result);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
read_and_unprotect (const char *fname)
|
||||
{
|
||||
int rc;
|
||||
unsigned char *key;
|
||||
unsigned char *result;
|
||||
size_t resultlen;
|
||||
|
||||
key = read_key (fname);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
rc = agent_unprotect (key, get_passphrase (), &result, &resultlen);
|
||||
xfree (key);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("unprotecting the key failed: %s\n", gnupg_strerror (rc));
|
||||
return;
|
||||
}
|
||||
|
||||
if (opt_armor)
|
||||
{
|
||||
char *p = make_advanced (result, resultlen);
|
||||
xfree (result);
|
||||
if (!p)
|
||||
return;
|
||||
result = p;
|
||||
resultlen = strlen (p);
|
||||
}
|
||||
|
||||
fwrite (result, resultlen, 1, stdout);
|
||||
xfree (result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
read_and_shadow (const char *fname)
|
||||
{
|
||||
int rc;
|
||||
unsigned char *key;
|
||||
unsigned char *result;
|
||||
size_t resultlen;
|
||||
|
||||
key = read_key (fname);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
rc = agent_shadow_key (key, "(8:313233342:43)", &result);
|
||||
xfree (key);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("shadowing the key failed: %s\n", gnupg_strerror (rc));
|
||||
return;
|
||||
}
|
||||
resultlen = gcry_sexp_canon_len (result, 0, NULL,NULL);
|
||||
assert (resultlen);
|
||||
|
||||
if (opt_armor)
|
||||
{
|
||||
char *p = make_advanced (result, resultlen);
|
||||
xfree (result);
|
||||
if (!p)
|
||||
return;
|
||||
result = p;
|
||||
resultlen = strlen (p);
|
||||
}
|
||||
|
||||
fwrite (result, resultlen, 1, stdout);
|
||||
xfree (result);
|
||||
}
|
||||
|
||||
static void
|
||||
show_shadow_info (const char *fname)
|
||||
{
|
||||
int rc;
|
||||
unsigned char *key;
|
||||
const unsigned char *info;
|
||||
size_t infolen;
|
||||
|
||||
key = read_key (fname);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
rc = agent_get_shadow_info (key, &info);
|
||||
xfree (key);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("get_shadow_info failed: %s\n", gnupg_strerror (rc));
|
||||
return;
|
||||
}
|
||||
infolen = gcry_sexp_canon_len (info, 0, NULL,NULL);
|
||||
assert (infolen);
|
||||
|
||||
if (opt_armor)
|
||||
{
|
||||
char *p = make_advanced (info, infolen);
|
||||
if (!p)
|
||||
return;
|
||||
fwrite (p, strlen (p), 1, stdout);
|
||||
xfree (p);
|
||||
}
|
||||
else
|
||||
fwrite (info, infolen, 1, stdout);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
show_file (const char *fname)
|
||||
{
|
||||
unsigned char *key;
|
||||
size_t keylen;
|
||||
char *p;
|
||||
|
||||
key = read_key (fname);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
keylen = gcry_sexp_canon_len (key, 0, NULL,NULL);
|
||||
assert (keylen);
|
||||
|
||||
p = make_advanced (key, keylen);
|
||||
xfree (key);
|
||||
if (p)
|
||||
{
|
||||
fwrite (p, strlen (p), 1, stdout);
|
||||
xfree (p);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
show_keygrip (const char *fname)
|
||||
{
|
||||
unsigned char *key;
|
||||
GcrySexp private;
|
||||
unsigned char grip[20];
|
||||
int i;
|
||||
|
||||
key = read_key (fname);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
if (gcry_sexp_new (&private, key, 0, 0))
|
||||
{
|
||||
log_error ("gcry_sexp_new failed\n");
|
||||
return;
|
||||
}
|
||||
xfree (key);
|
||||
|
||||
if (!gcry_pk_get_keygrip (private, grip))
|
||||
{
|
||||
log_error ("can't calculate keygrip\n");
|
||||
return;
|
||||
}
|
||||
gcry_sexp_release (private);
|
||||
|
||||
for (i=0; i < 20; i++)
|
||||
printf ("%02X", grip[i]);
|
||||
putchar ('\n');
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
rsa_key_check (struct rsa_secret_key_s *skey)
|
||||
{
|
||||
int err = 0;
|
||||
MPI t = gcry_mpi_snew (0);
|
||||
MPI t1 = gcry_mpi_snew (0);
|
||||
MPI t2 = gcry_mpi_snew (0);
|
||||
MPI phi = gcry_mpi_snew (0);
|
||||
|
||||
/* check that n == p * q */
|
||||
gcry_mpi_mul (t, skey->p, skey->q);
|
||||
if (gcry_mpi_cmp( t, skey->n) )
|
||||
{
|
||||
log_error ("RSA oops: n != p * q\n");
|
||||
err++;
|
||||
}
|
||||
|
||||
/* check that p is less than q */
|
||||
if (gcry_mpi_cmp (skey->p, skey->q) > 0)
|
||||
{
|
||||
GcryMPI tmp;
|
||||
|
||||
log_info ("swapping secret primes\n");
|
||||
tmp = gcry_mpi_copy (skey->p);
|
||||
gcry_mpi_set (skey->p, skey->q);
|
||||
gcry_mpi_set (skey->q, tmp);
|
||||
gcry_mpi_release (tmp);
|
||||
/* and must recompute u of course */
|
||||
gcry_mpi_invm (skey->u, skey->p, skey->q);
|
||||
}
|
||||
|
||||
/* check that e divides neither p-1 nor q-1 */
|
||||
gcry_mpi_sub_ui (t, skey->p, 1 );
|
||||
gcry_mpi_div (NULL, t, t, skey->e, 0);
|
||||
if (!gcry_mpi_cmp_ui( t, 0) )
|
||||
{
|
||||
log_error ("RSA oops: e divides p-1\n");
|
||||
err++;
|
||||
}
|
||||
gcry_mpi_sub_ui (t, skey->q, 1);
|
||||
gcry_mpi_div (NULL, t, t, skey->e, 0);
|
||||
if (!gcry_mpi_cmp_ui( t, 0))
|
||||
{
|
||||
log_info ( "RSA oops: e divides q-1\n" );
|
||||
err++;
|
||||
}
|
||||
|
||||
/* check that d is correct. */
|
||||
gcry_mpi_sub_ui (t1, skey->p, 1);
|
||||
gcry_mpi_sub_ui (t2, skey->q, 1);
|
||||
gcry_mpi_mul (phi, t1, t2);
|
||||
gcry_mpi_invm (t, skey->e, phi);
|
||||
if (gcry_mpi_cmp (t, skey->d))
|
||||
{ /* no: try universal exponent. */
|
||||
gcry_mpi_gcd (t, t1, t2);
|
||||
gcry_mpi_div (t, NULL, phi, t, 0);
|
||||
gcry_mpi_invm (t, skey->e, t);
|
||||
if (gcry_mpi_cmp (t, skey->d))
|
||||
{
|
||||
log_error ("RSA oops: bad secret exponent\n");
|
||||
err++;
|
||||
}
|
||||
}
|
||||
|
||||
/* check for correctness of u */
|
||||
gcry_mpi_invm (t, skey->p, skey->q);
|
||||
if (gcry_mpi_cmp (t, skey->u))
|
||||
{
|
||||
log_info ( "RSA oops: bad u parameter\n");
|
||||
err++;
|
||||
}
|
||||
|
||||
if (err)
|
||||
log_info ("RSA secret key check failed\n");
|
||||
|
||||
gcry_mpi_release (t);
|
||||
gcry_mpi_release (t1);
|
||||
gcry_mpi_release (t2);
|
||||
gcry_mpi_release (phi);
|
||||
|
||||
return err? -1:0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
import_p12_file (const char *fname)
|
||||
{
|
||||
char *buf;
|
||||
unsigned char *result;
|
||||
size_t buflen, resultlen;
|
||||
int i;
|
||||
int rc;
|
||||
GcryMPI *kparms;
|
||||
struct rsa_secret_key_s sk;
|
||||
GcrySexp s_key;
|
||||
unsigned char *key;
|
||||
unsigned char grip[20];
|
||||
|
||||
/* fixme: we should release some stuff on error */
|
||||
|
||||
buf = read_file (fname, &buflen);
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
kparms = p12_parse (buf, buflen, get_passphrase ());
|
||||
xfree (buf);
|
||||
if (!kparms)
|
||||
{
|
||||
log_error ("error parsing or decrypting the PKCS-1 file\n");
|
||||
return;
|
||||
}
|
||||
for (i=0; kparms[i]; i++)
|
||||
;
|
||||
if (i != 8)
|
||||
{
|
||||
log_error ("invalid structure of private key\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* print_mpi (" n", kparms[0]); */
|
||||
/* print_mpi (" e", kparms[1]); */
|
||||
/* print_mpi (" d", kparms[2]); */
|
||||
/* print_mpi (" p", kparms[3]); */
|
||||
/* print_mpi (" q", kparms[4]); */
|
||||
/* print_mpi ("dmp1", kparms[5]); */
|
||||
/* print_mpi ("dmq1", kparms[6]); */
|
||||
/* print_mpi (" u", kparms[7]); */
|
||||
|
||||
sk.n = kparms[0];
|
||||
sk.e = kparms[1];
|
||||
sk.d = kparms[2];
|
||||
sk.q = kparms[3];
|
||||
sk.p = kparms[4];
|
||||
sk.u = kparms[7];
|
||||
if (rsa_key_check (&sk))
|
||||
return;
|
||||
/* print_mpi (" n", sk.n); */
|
||||
/* print_mpi (" e", sk.e); */
|
||||
/* print_mpi (" d", sk.d); */
|
||||
/* print_mpi (" p", sk.p); */
|
||||
/* print_mpi (" q", sk.q); */
|
||||
/* print_mpi (" u", sk.u); */
|
||||
|
||||
/* Create an S-expresion from the parameters. */
|
||||
rc = gcry_sexp_build (&s_key, NULL,
|
||||
"(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
|
||||
sk.n, sk.e, sk.d, sk.p, sk.q, sk.u, NULL);
|
||||
for (i=0; i < 8; i++)
|
||||
gcry_mpi_release (kparms[i]);
|
||||
gcry_free (kparms);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to created S-expression from key: %s\n",
|
||||
gcry_strerror (rc));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Compute the keygrip. */
|
||||
if (!gcry_pk_get_keygrip (s_key, grip))
|
||||
{
|
||||
log_error ("can't calculate keygrip\n");
|
||||
return;
|
||||
}
|
||||
log_info ("keygrip: ");
|
||||
for (i=0; i < 20; i++)
|
||||
log_printf ("%02X", grip[i]);
|
||||
log_printf ("\n");
|
||||
|
||||
/* convert to canonical encoding */
|
||||
buflen = gcry_sexp_sprint (s_key, GCRYSEXP_FMT_CANON, NULL, 0);
|
||||
assert (buflen);
|
||||
key = gcry_xmalloc_secure (buflen);
|
||||
buflen = gcry_sexp_sprint (s_key, GCRYSEXP_FMT_CANON, key, buflen);
|
||||
assert (buflen);
|
||||
gcry_sexp_release (s_key);
|
||||
|
||||
|
||||
rc = agent_protect (key, get_passphrase (), &result, &resultlen);
|
||||
xfree (key);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("protecting the key failed: %s\n", gnupg_strerror (rc));
|
||||
return;
|
||||
}
|
||||
|
||||
if (opt_armor)
|
||||
{
|
||||
char *p = make_advanced (result, resultlen);
|
||||
xfree (result);
|
||||
if (!p)
|
||||
return;
|
||||
result = p;
|
||||
resultlen = strlen (p);
|
||||
}
|
||||
|
||||
if (opt_store)
|
||||
store_private_key (grip, result, resultlen, opt_force);
|
||||
else
|
||||
fwrite (result, resultlen, 1, stdout);
|
||||
|
||||
xfree (result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static GcryMPI *
|
||||
sexp_to_kparms (GCRY_SEXP sexp)
|
||||
{
|
||||
GcrySexp list, l2;
|
||||
const char *name;
|
||||
const char *s;
|
||||
size_t n;
|
||||
int i, idx;
|
||||
const char *elems;
|
||||
GcryMPI *array;
|
||||
|
||||
list = gcry_sexp_find_token (sexp, "private-key", 0 );
|
||||
if(!list)
|
||||
return NULL;
|
||||
l2 = gcry_sexp_cadr (list);
|
||||
gcry_sexp_release (list);
|
||||
list = l2;
|
||||
name = gcry_sexp_nth_data (list, 0, &n);
|
||||
if(!name || n != 3 || memcmp (name, "rsa", 3))
|
||||
{
|
||||
gcry_sexp_release (list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Paramter names used with RSA. */
|
||||
elems = "nedpqu";
|
||||
array = xcalloc (strlen(elems) + 1, sizeof *array);
|
||||
for (idx=0, s=elems; *s; s++, idx++ )
|
||||
{
|
||||
l2 = gcry_sexp_find_token (list, s, 1);
|
||||
if (!l2)
|
||||
{
|
||||
for (i=0; i<idx; i++)
|
||||
gcry_mpi_release (array[i]);
|
||||
xfree (array);
|
||||
gcry_sexp_release (list);
|
||||
return NULL; /* required parameter not found */
|
||||
}
|
||||
array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
|
||||
gcry_sexp_release (l2);
|
||||
if (!array[idx])
|
||||
{
|
||||
for (i=0; i<idx; i++)
|
||||
gcry_mpi_release (array[i]);
|
||||
xfree (array);
|
||||
gcry_sexp_release (list);
|
||||
return NULL; /* required parameter is invalid */
|
||||
}
|
||||
}
|
||||
|
||||
gcry_sexp_release (list);
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
export_p12_file (const char *fname)
|
||||
{
|
||||
GcryMPI kparms[9], *kp;
|
||||
unsigned char *key;
|
||||
size_t keylen;
|
||||
GcrySexp private;
|
||||
struct rsa_secret_key_s sk;
|
||||
int i;
|
||||
|
||||
key = read_key (fname);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
if (gcry_sexp_new (&private, key, 0, 0))
|
||||
{
|
||||
log_error ("gcry_sexp_new failed\n");
|
||||
return;
|
||||
}
|
||||
xfree (key);
|
||||
|
||||
kp = sexp_to_kparms (private);
|
||||
gcry_sexp_release (private);
|
||||
if (!kp)
|
||||
{
|
||||
log_error ("error converting key parameters\n");
|
||||
return;
|
||||
}
|
||||
sk.n = kp[0];
|
||||
sk.e = kp[1];
|
||||
sk.d = kp[2];
|
||||
sk.p = kp[3];
|
||||
sk.q = kp[4];
|
||||
sk.u = kp[5];
|
||||
xfree (kp);
|
||||
|
||||
|
||||
kparms[0] = sk.n;
|
||||
kparms[1] = sk.e;
|
||||
kparms[2] = sk.d;
|
||||
kparms[3] = sk.q;
|
||||
kparms[4] = sk.p;
|
||||
kparms[5] = gcry_mpi_snew (0); /* compute d mod (p-1) */
|
||||
gcry_mpi_sub_ui (kparms[5], kparms[3], 1);
|
||||
gcry_mpi_mod (kparms[5], sk.d, kparms[5]);
|
||||
kparms[6] = gcry_mpi_snew (0); /* compute d mod (q-1) */
|
||||
gcry_mpi_sub_ui (kparms[6], kparms[4], 1);
|
||||
gcry_mpi_mod (kparms[6], sk.d, kparms[6]);
|
||||
kparms[7] = sk.u;
|
||||
kparms[8] = NULL;
|
||||
|
||||
key = p12_build (kparms, get_passphrase (), &keylen);
|
||||
for (i=0; i < 8; i++)
|
||||
gcry_mpi_release (kparms[i]);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
fwrite (key, keylen, 1, stdout);
|
||||
xfree (key);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv )
|
||||
{
|
||||
ARGPARSE_ARGS pargs;
|
||||
int cmd = 0;
|
||||
|
||||
set_strusage (my_strusage);
|
||||
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
|
||||
log_set_prefix ("gpg-protect-tool", 1);
|
||||
i18n_init ();
|
||||
|
||||
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
|
||||
{
|
||||
log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
|
||||
NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
|
||||
}
|
||||
|
||||
gcry_set_log_handler (my_gcry_logger, NULL);
|
||||
|
||||
gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
|
||||
|
||||
pargs.argc = &argc;
|
||||
pargs.argv = &argv;
|
||||
pargs.flags= 1; /* do not remove the args */
|
||||
while (arg_parse (&pargs, opts) )
|
||||
{
|
||||
switch (pargs.r_opt)
|
||||
{
|
||||
case oVerbose: opt.verbose++; break;
|
||||
case oArmor: opt_armor=1; break;
|
||||
|
||||
case oProtect: cmd = oProtect; break;
|
||||
case oUnprotect: cmd = oUnprotect; break;
|
||||
case oShadow: cmd = oShadow; break;
|
||||
case oShowShadowInfo: cmd = oShowShadowInfo; break;
|
||||
case oShowKeygrip: cmd = oShowKeygrip; break;
|
||||
case oP12Import: cmd = oP12Import; break;
|
||||
case oP12Export: cmd = oP12Export; break;
|
||||
|
||||
case oPassphrase: passphrase = pargs.r.ret_str; break;
|
||||
case oStore: opt_store = 1; break;
|
||||
case oForce: opt_force = 1; break;
|
||||
|
||||
default : pargs.err = 2; break;
|
||||
}
|
||||
}
|
||||
if (log_get_errorcount(0))
|
||||
exit(2);
|
||||
|
||||
if (argc != 1)
|
||||
usage (1);
|
||||
|
||||
if (cmd == oProtect)
|
||||
read_and_protect (*argv);
|
||||
else if (cmd == oUnprotect)
|
||||
read_and_unprotect (*argv);
|
||||
else if (cmd == oShadow)
|
||||
read_and_shadow (*argv);
|
||||
else if (cmd == oShowShadowInfo)
|
||||
show_shadow_info (*argv);
|
||||
else if (cmd == oShowKeygrip)
|
||||
show_keygrip (*argv);
|
||||
else if (cmd == oP12Import)
|
||||
import_p12_file (*argv);
|
||||
else if (cmd == oP12Export)
|
||||
export_p12_file (*argv);
|
||||
else
|
||||
show_file (*argv);
|
||||
|
||||
agent_exit (0);
|
||||
return 8; /*NOTREACHED*/
|
||||
}
|
||||
|
||||
void
|
||||
agent_exit (int rc)
|
||||
{
|
||||
rc = rc? rc : log_get_errorcount(0)? 2 : 0;
|
||||
exit (rc);
|
||||
}
|
||||
|
||||
|
||||
/* Return the passphrase string and ask the agent if it has not been
|
||||
set from the command line. */
|
||||
static const char *
|
||||
get_passphrase (void)
|
||||
{
|
||||
char *pw;
|
||||
int err;
|
||||
|
||||
if (passphrase)
|
||||
return passphrase;
|
||||
|
||||
pw = simple_pwquery (NULL,NULL,
|
||||
_("Enter passphrase:"),
|
||||
_("Please enter the passphrase or the PIN\n"
|
||||
"needed to complete this operation."),
|
||||
&err);
|
||||
if (!pw)
|
||||
{
|
||||
if (err)
|
||||
log_error ("error while asking for the passphrase\n");
|
||||
else
|
||||
log_info ("cancelled\n");
|
||||
agent_exit (0);
|
||||
}
|
||||
passphrase = pw;
|
||||
return passphrase;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
store_private_key (const unsigned char *grip,
|
||||
const void *buffer, size_t length, int force)
|
||||
{
|
||||
int i;
|
||||
const char *homedir;
|
||||
char *fname;
|
||||
FILE *fp;
|
||||
char hexgrip[40+4+1];
|
||||
|
||||
for (i=0; i < 20; i++)
|
||||
sprintf (hexgrip+2*i, "%02X", grip[i]);
|
||||
strcpy (hexgrip+40, ".key");
|
||||
|
||||
homedir = getenv("GNUPGHOME");
|
||||
if (!homedir || !*homedir)
|
||||
homedir = GNUPG_DEFAULT_HOMEDIR;
|
||||
|
||||
fname = make_filename (homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
|
||||
if (force)
|
||||
fp = fopen (fname, "wb");
|
||||
else
|
||||
{
|
||||
if (!access (fname, F_OK))
|
||||
{
|
||||
log_error ("secret key file `%s' already exists\n", fname);
|
||||
xfree (fname);
|
||||
return -1;
|
||||
}
|
||||
fp = fopen (fname, "wbx"); /* FIXME: the x is a GNU extension - let
|
||||
configure check whether this actually
|
||||
works */
|
||||
}
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
log_error ("can't create `%s': %s\n", fname, strerror (errno));
|
||||
xfree (fname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fwrite (buffer, length, 1, fp) != 1)
|
||||
{
|
||||
log_error ("error writing `%s': %s\n", fname, strerror (errno));
|
||||
fclose (fp);
|
||||
remove (fname);
|
||||
xfree (fname);
|
||||
return -1;
|
||||
}
|
||||
if ( fclose (fp) )
|
||||
{
|
||||
log_error ("error closing `%s': %s\n", fname, strerror (errno));
|
||||
remove (fname);
|
||||
xfree (fname);
|
||||
return -1;
|
||||
}
|
||||
log_info ("secret key stored as `%s'\n", fname);
|
||||
|
||||
xfree (fname);
|
||||
return 0;
|
||||
}
|
967
agent/protect.c
967
agent/protect.c
|
@ -1,967 +0,0 @@
|
|||
/* protect.c - Un/Protect a secret key
|
||||
* Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "agent.h"
|
||||
|
||||
#include "sexp-parse.h"
|
||||
|
||||
#define PROT_CIPHER GCRY_CIPHER_AES
|
||||
#define PROT_CIPHER_STRING "aes"
|
||||
#define PROT_CIPHER_KEYLEN (128/8)
|
||||
|
||||
|
||||
/* A table containing the information needed to create a protected
|
||||
private key */
|
||||
static struct {
|
||||
const char *algo;
|
||||
const char *parmlist;
|
||||
int prot_from, prot_to;
|
||||
} protect_info[] = {
|
||||
{ "rsa", "nedpqu", 2, 5 },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
||||
static int
|
||||
hash_passphrase (const char *passphrase, int hashalgo,
|
||||
int s2kmode,
|
||||
const unsigned char *s2ksalt, unsigned long s2kcount,
|
||||
unsigned char *key, size_t keylen);
|
||||
|
||||
|
||||
|
||||
/* Calculate the MIC for a private key S-Exp. SHA1HASH should pint to
|
||||
a 20 byte buffer. This function is suitable for any algorithms. */
|
||||
static int
|
||||
calculate_mic (const unsigned char *plainkey, unsigned char *sha1hash)
|
||||
{
|
||||
const unsigned char *hash_begin, *hash_end;
|
||||
const unsigned char *s;
|
||||
size_t n;
|
||||
|
||||
s = plainkey;
|
||||
if (*s != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (!smatch (&s, n, "private-key"))
|
||||
return GNUPG_Unknown_Sexp;
|
||||
if (*s != '(')
|
||||
return GNUPG_Unknown_Sexp;
|
||||
hash_begin = s;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s += n; /* skip over the algorithm name */
|
||||
|
||||
while (*s == '(')
|
||||
{
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s += n;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s += n;
|
||||
if ( *s != ')' )
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s++;
|
||||
}
|
||||
if (*s != ')')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s++;
|
||||
hash_end = s;
|
||||
|
||||
gcry_md_hash_buffer (GCRY_MD_SHA1, sha1hash,
|
||||
hash_begin, hash_end - hash_begin);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Encrypt the parameter block starting at PROTBEGIN with length
|
||||
PROTLEN using the utf8 encoded key PASSPHRASE and return the entire
|
||||
encrypted block in RESULT or ereturn with an error code. SHA1HASH
|
||||
is the 20 byte SHA-1 hash required for the integrity code.
|
||||
|
||||
The parameter block is expected to be an incomplete S-Expression of
|
||||
the form (example in advanced format):
|
||||
|
||||
(d #046129F..[some bytes not shown]..81#)
|
||||
(p #00e861b..[some bytes not shown]..f1#)
|
||||
(q #00f7a7c..[some bytes not shown]..61#)
|
||||
(u #304559a..[some bytes not shown]..9b#)
|
||||
|
||||
the returned block is the S-Expression:
|
||||
|
||||
(protected mode (parms) encrypted_octet_string)
|
||||
|
||||
*/
|
||||
static int
|
||||
do_encryption (const char *protbegin, size_t protlen,
|
||||
const char *passphrase, const unsigned char *sha1hash,
|
||||
unsigned char **result, size_t *resultlen)
|
||||
{
|
||||
GCRY_CIPHER_HD hd;
|
||||
const char *modestr = "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc";
|
||||
int blklen, enclen, outlen;
|
||||
char *iv = NULL;
|
||||
int rc = 0;
|
||||
char *outbuf = NULL;
|
||||
char *p;
|
||||
int saltpos, ivpos, encpos;
|
||||
|
||||
hd = gcry_cipher_open (PROT_CIPHER, GCRY_CIPHER_MODE_CBC,
|
||||
GCRY_CIPHER_SECURE);
|
||||
if (!hd)
|
||||
return map_gcry_err (gcry_errno());
|
||||
|
||||
|
||||
/* We need to work on a copy of the data because this makes it
|
||||
easier to add the trailer and the padding and more important we
|
||||
have to prefix the text with 2 parenthesis, so we have to
|
||||
allocate enough space for:
|
||||
|
||||
((<parameter_list>)(4:hash4:sha120:<hashvalue>)) + padding
|
||||
|
||||
We always append a full block of random bytes as padding but
|
||||
encrypt only what is needed for a full blocksize */
|
||||
blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER);
|
||||
outlen = 2 + protlen + 2 + 6 + 6 + 23 + 2 + blklen;
|
||||
enclen = outlen/blklen * blklen;
|
||||
outbuf = gcry_malloc_secure (outlen);
|
||||
if (!outbuf)
|
||||
rc = GNUPG_Out_Of_Core;
|
||||
if (!rc)
|
||||
{
|
||||
/* allocate random bytes to be used as IV, padding and s2k salt*/
|
||||
iv = gcry_random_bytes (blklen*2+8, GCRY_WEAK_RANDOM);
|
||||
if (!iv)
|
||||
rc = GNUPG_Out_Of_Core;
|
||||
else
|
||||
rc = gcry_cipher_setiv (hd, iv, blklen);
|
||||
}
|
||||
if (!rc)
|
||||
{
|
||||
unsigned char *key;
|
||||
size_t keylen = PROT_CIPHER_KEYLEN;
|
||||
|
||||
key = gcry_malloc_secure (keylen);
|
||||
if (!key)
|
||||
rc = GNUPG_Out_Of_Core;
|
||||
else
|
||||
{
|
||||
rc = hash_passphrase (passphrase, GCRY_MD_SHA1,
|
||||
3, iv+2*blklen, 96, key, keylen);
|
||||
if (!rc)
|
||||
rc = gcry_cipher_setkey (hd, key, keylen);
|
||||
xfree (key);
|
||||
}
|
||||
}
|
||||
if (!rc)
|
||||
{
|
||||
p = outbuf;
|
||||
*p++ = '(';
|
||||
*p++ = '(';
|
||||
memcpy (p, protbegin, protlen);
|
||||
p += protlen;
|
||||
memcpy (p, ")(4:hash4:sha120:", 17);
|
||||
p += 17;
|
||||
memcpy (p, sha1hash, 20);
|
||||
p += 20;
|
||||
*p++ = ')';
|
||||
*p++ = ')';
|
||||
memcpy (p, iv+blklen, blklen);
|
||||
p += blklen;
|
||||
assert ( p - outbuf == outlen);
|
||||
rc = gcry_cipher_encrypt (hd, outbuf, enclen, NULL, 0);
|
||||
}
|
||||
gcry_cipher_close (hd);
|
||||
if (rc)
|
||||
{
|
||||
xfree (iv);
|
||||
xfree (outbuf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Now allocate the buffer we want to return. This is
|
||||
|
||||
(protected openpgp-s2k3-sha1-aes-cbc
|
||||
((sha1 salt no_of_iterations) 16byte_iv)
|
||||
encrypted_octet_string)
|
||||
|
||||
in canoncical format of course. We use asprintf and %n modifier
|
||||
and spaces as palceholders. */
|
||||
asprintf (&p,
|
||||
"(9:protected%d:%s((4:sha18:%n_8bytes_2:96)%d:%n%*s)%d:%n%*s)",
|
||||
(int)strlen (modestr), modestr,
|
||||
&saltpos,
|
||||
blklen, &ivpos, blklen, "",
|
||||
enclen, &encpos, enclen, "");
|
||||
if (p)
|
||||
{ /* asprintf does not use out malloc system */
|
||||
char *psave = p;
|
||||
p = xtrymalloc (strlen (psave)+1);
|
||||
if (p)
|
||||
strcpy (p, psave);
|
||||
free (psave);
|
||||
}
|
||||
if (!p)
|
||||
{
|
||||
xfree (iv);
|
||||
xfree (outbuf);
|
||||
return GNUPG_Out_Of_Core;
|
||||
}
|
||||
*resultlen = strlen (p);
|
||||
*result = p;
|
||||
memcpy (p+saltpos, iv+2*blklen, 8);
|
||||
memcpy (p+ivpos, iv, blklen);
|
||||
memcpy (p+encpos, outbuf, enclen);
|
||||
xfree (iv);
|
||||
xfree (outbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Protect the key encoded in canonical format in plainkey. We assume
|
||||
a valid S-Exp here. */
|
||||
int
|
||||
agent_protect (const unsigned char *plainkey, const char *passphrase,
|
||||
unsigned char **result, size_t *resultlen)
|
||||
{
|
||||
int rc;
|
||||
const unsigned char *s;
|
||||
const unsigned char *hash_begin, *hash_end;
|
||||
const unsigned char *prot_begin, *prot_end, *real_end;
|
||||
size_t n;
|
||||
int c, infidx, i;
|
||||
unsigned char hashvalue[20];
|
||||
unsigned char *protected;
|
||||
size_t protectedlen;
|
||||
int depth = 0;
|
||||
unsigned char *p;
|
||||
|
||||
s = plainkey;
|
||||
if (*s != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
depth++;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (!smatch (&s, n, "private-key"))
|
||||
return GNUPG_Unknown_Sexp;
|
||||
if (*s != '(')
|
||||
return GNUPG_Unknown_Sexp;
|
||||
depth++;
|
||||
hash_begin = s;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
|
||||
for (infidx=0; protect_info[infidx].algo
|
||||
&& !smatch (&s, n, protect_info[infidx].algo); infidx++)
|
||||
;
|
||||
if (!protect_info[infidx].algo)
|
||||
return GNUPG_Unsupported_Algorithm;
|
||||
|
||||
prot_begin = prot_end = NULL;
|
||||
for (i=0; (c=protect_info[infidx].parmlist[i]); i++)
|
||||
{
|
||||
if (i == protect_info[infidx].prot_from)
|
||||
prot_begin = s;
|
||||
if (*s != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
depth++;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (n != 1 || c != *s)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s += n;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s +=n; /* skip value */
|
||||
if (*s != ')')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
depth--;
|
||||
if (i == protect_info[infidx].prot_to)
|
||||
prot_end = s;
|
||||
s++;
|
||||
}
|
||||
if (*s != ')' || !prot_begin || !prot_end )
|
||||
return GNUPG_Invalid_Sexp;
|
||||
depth--;
|
||||
hash_end = s;
|
||||
s++;
|
||||
/* skip to the end of the S-exp */
|
||||
assert (depth == 1);
|
||||
rc = sskip (&s, &depth);
|
||||
if (rc)
|
||||
return rc;
|
||||
assert (!depth);
|
||||
real_end = s-1;
|
||||
|
||||
gcry_md_hash_buffer (GCRY_MD_SHA1, hashvalue,
|
||||
hash_begin, hash_end - hash_begin + 1);
|
||||
|
||||
rc = do_encryption (prot_begin, prot_end - prot_begin + 1,
|
||||
passphrase, hashvalue,
|
||||
&protected, &protectedlen);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Now create the protected version of the key. Note that the 10
|
||||
extra bytes are for for the inserted "protected-" string (the
|
||||
beginning of the plaintext reads: "((11:private-key(" ). */
|
||||
*resultlen = (10
|
||||
+ (prot_begin-plainkey)
|
||||
+ protectedlen
|
||||
+ (real_end-prot_end));
|
||||
*result = p = xtrymalloc (*resultlen);
|
||||
if (!p)
|
||||
{
|
||||
xfree (protected);
|
||||
return GNUPG_Out_Of_Core;
|
||||
}
|
||||
memcpy (p, "(21:protected-", 14);
|
||||
p += 14;
|
||||
memcpy (p, plainkey+4, prot_begin - plainkey - 4);
|
||||
p += prot_begin - plainkey - 4;
|
||||
memcpy (p, protected, protectedlen);
|
||||
p += protectedlen;
|
||||
memcpy (p, prot_end+1, real_end - prot_end);
|
||||
p += real_end - prot_end;
|
||||
assert ( p - *result == *resultlen);
|
||||
xfree (protected);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Do the actual decryption and check the return list for consistency. */
|
||||
static int
|
||||
do_decryption (const unsigned char *protected, size_t protectedlen,
|
||||
const char *passphrase,
|
||||
const unsigned char *s2ksalt, unsigned long s2kcount,
|
||||
const unsigned char *iv, size_t ivlen,
|
||||
unsigned char **result)
|
||||
{
|
||||
int rc = 0;
|
||||
int blklen;
|
||||
GCRY_CIPHER_HD hd;
|
||||
unsigned char *outbuf;
|
||||
size_t reallen;
|
||||
|
||||
blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER);
|
||||
if (protectedlen < 4 || (protectedlen%blklen))
|
||||
return GNUPG_Corrupted_Protection;
|
||||
|
||||
hd = gcry_cipher_open (PROT_CIPHER, GCRY_CIPHER_MODE_CBC,
|
||||
GCRY_CIPHER_SECURE);
|
||||
if (!hd)
|
||||
return map_gcry_err (gcry_errno());
|
||||
|
||||
outbuf = gcry_malloc_secure (protectedlen);
|
||||
if (!outbuf)
|
||||
rc = GNUPG_Out_Of_Core;
|
||||
if (!rc)
|
||||
rc = gcry_cipher_setiv (hd, iv, ivlen);
|
||||
if (!rc)
|
||||
{
|
||||
unsigned char *key;
|
||||
size_t keylen = PROT_CIPHER_KEYLEN;
|
||||
|
||||
key = gcry_malloc_secure (keylen);
|
||||
if (!key)
|
||||
rc = GNUPG_Out_Of_Core;
|
||||
else
|
||||
{
|
||||
rc = hash_passphrase (passphrase, GCRY_MD_SHA1,
|
||||
3, s2ksalt, s2kcount, key, keylen);
|
||||
if (!rc)
|
||||
rc = gcry_cipher_setkey (hd, key, keylen);
|
||||
xfree (key);
|
||||
}
|
||||
}
|
||||
if (!rc)
|
||||
rc = gcry_cipher_decrypt (hd, outbuf, protectedlen,
|
||||
protected, protectedlen);
|
||||
gcry_cipher_close (hd);
|
||||
if (rc)
|
||||
{
|
||||
xfree (outbuf);
|
||||
return rc;
|
||||
}
|
||||
/* do a quick check first */
|
||||
if (*outbuf != '(' && outbuf[1] != '(')
|
||||
{
|
||||
xfree (outbuf);
|
||||
return GNUPG_Bad_Passphrase;
|
||||
}
|
||||
/* check that we have a consistent S-Exp */
|
||||
reallen = gcry_sexp_canon_len (outbuf, protectedlen, NULL, NULL);
|
||||
if (!reallen || (reallen + blklen < protectedlen) )
|
||||
{
|
||||
xfree (outbuf);
|
||||
return GNUPG_Bad_Passphrase;
|
||||
}
|
||||
*result = outbuf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Merge the parameter list contained in CLEARTEXT with the original
|
||||
protect lists PROTECTEDKEY by replacing the list at REPLACEPOS.
|
||||
Return the new list in RESULT and the MIC value in the 20 byte
|
||||
buffer SHA1HASH. */
|
||||
static int
|
||||
merge_lists (const unsigned char *protectedkey,
|
||||
size_t replacepos,
|
||||
const unsigned char *cleartext,
|
||||
unsigned char *sha1hash, unsigned char **result)
|
||||
{
|
||||
size_t n, newlistlen;
|
||||
unsigned char *newlist, *p;
|
||||
const unsigned char *s;
|
||||
const unsigned char *startpos, *endpos;
|
||||
int i, rc;
|
||||
|
||||
if (replacepos < 26)
|
||||
return GNUPG_Bug;
|
||||
|
||||
/* Estimate the required size of the resulting list. We have a large
|
||||
safety margin of >20 bytes (MIC hash from CLEARTEXT and the
|
||||
removed "protected-" */
|
||||
newlistlen = gcry_sexp_canon_len (protectedkey, 0, NULL, NULL);
|
||||
if (!newlistlen)
|
||||
return GNUPG_Bug;
|
||||
n = gcry_sexp_canon_len (cleartext, 0, NULL, NULL);
|
||||
if (!n)
|
||||
return GNUPG_Bug;
|
||||
newlistlen += n;
|
||||
newlist = gcry_malloc_secure (newlistlen);
|
||||
if (!newlist)
|
||||
return GNUPG_Out_Of_Core;
|
||||
|
||||
/* Copy the initial segment */
|
||||
strcpy (newlist, "(11:private-key");
|
||||
p = newlist + 15;
|
||||
memcpy (p, protectedkey+15+10, replacepos-15-10);
|
||||
p += replacepos-15-10;
|
||||
|
||||
/* copy the cleartext */
|
||||
s = cleartext;
|
||||
if (*s != '(' && s[1] != '(')
|
||||
return GNUPG_Bug; /*we already checked this */
|
||||
s += 2;
|
||||
startpos = s;
|
||||
while ( *s == '(' )
|
||||
{
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
goto invalid_sexp;
|
||||
s += n;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
goto invalid_sexp;
|
||||
s += n;
|
||||
if ( *s != ')' )
|
||||
goto invalid_sexp;
|
||||
s++;
|
||||
}
|
||||
if ( *s != ')' )
|
||||
goto invalid_sexp;
|
||||
endpos = s;
|
||||
s++;
|
||||
/* short intermezzo: Get the MIC */
|
||||
if (*s != '(')
|
||||
goto invalid_sexp;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!smatch (&s, n, "hash"))
|
||||
goto invalid_sexp;
|
||||
n = snext (&s);
|
||||
if (!smatch (&s, n, "sha1"))
|
||||
goto invalid_sexp;
|
||||
n = snext (&s);
|
||||
if (n != 20)
|
||||
goto invalid_sexp;
|
||||
memcpy (sha1hash, s, 20);
|
||||
s += n;
|
||||
if (*s != ')')
|
||||
goto invalid_sexp;
|
||||
/* end intermezzo */
|
||||
|
||||
/* append the parameter list */
|
||||
memcpy (p, startpos, endpos - startpos);
|
||||
p += endpos - startpos;
|
||||
|
||||
/* skip overt the protected list element in the original list */
|
||||
s = protectedkey + replacepos;
|
||||
assert (*s == '(');
|
||||
s++;
|
||||
i = 1;
|
||||
rc = sskip (&s, &i);
|
||||
if (rc)
|
||||
goto failure;
|
||||
startpos = s;
|
||||
i = 2; /* we are inside this level */
|
||||
rc = sskip (&s, &i);
|
||||
if (rc)
|
||||
goto failure;
|
||||
assert (s[-1] == ')');
|
||||
endpos = s; /* one behind the end of the list */
|
||||
|
||||
/* append the rest */
|
||||
memcpy (p, startpos, endpos - startpos);
|
||||
p += endpos - startpos;
|
||||
|
||||
/* ready */
|
||||
*result = newlist;
|
||||
return 0;
|
||||
|
||||
failure:
|
||||
xfree (newlist);
|
||||
return rc;
|
||||
|
||||
invalid_sexp:
|
||||
xfree (newlist);
|
||||
return GNUPG_Invalid_Sexp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Unprotect the key encoded in canonical format. We assume a valid
|
||||
S-Exp here. */
|
||||
int
|
||||
agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
|
||||
unsigned char **result, size_t *resultlen)
|
||||
{
|
||||
int rc;
|
||||
const unsigned char *s;
|
||||
size_t n;
|
||||
int infidx, i;
|
||||
unsigned char sha1hash[20], sha1hash2[20];
|
||||
const unsigned char *s2ksalt;
|
||||
unsigned long s2kcount;
|
||||
const unsigned char *iv;
|
||||
const unsigned char *prot_begin;
|
||||
unsigned char *cleartext;
|
||||
unsigned char *final;
|
||||
|
||||
s = protectedkey;
|
||||
if (*s != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (!smatch (&s, n, "protected-private-key"))
|
||||
return GNUPG_Unknown_Sexp;
|
||||
if (*s != '(')
|
||||
return GNUPG_Unknown_Sexp;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
|
||||
for (infidx=0; protect_info[infidx].algo
|
||||
&& !smatch (&s, n, protect_info[infidx].algo); infidx++)
|
||||
;
|
||||
if (!protect_info[infidx].algo)
|
||||
return GNUPG_Unsupported_Algorithm;
|
||||
|
||||
/* now find the list with the protected information. Here is an
|
||||
example for such a list:
|
||||
(protected openpgp-s2k3-sha1-aes-cbc
|
||||
((sha1 <salt> <count>) <Initialization_Vector>)
|
||||
<encrypted_data>)
|
||||
*/
|
||||
for (;;)
|
||||
{
|
||||
if (*s != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
prot_begin = s;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (smatch (&s, n, "protected"))
|
||||
break;
|
||||
s += n;
|
||||
i = 1;
|
||||
rc = sskip (&s, &i);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
/* found */
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (!smatch (&s, n, "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc"))
|
||||
return GNUPG_Unsupported_Protection;
|
||||
if (*s != '(' || s[1] != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s += 2;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (!smatch (&s, n, "sha1"))
|
||||
return GNUPG_Unsupported_Protection;
|
||||
n = snext (&s);
|
||||
if (n != 8)
|
||||
return GNUPG_Corrupted_Protection;
|
||||
s2ksalt = s;
|
||||
s += n;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Corrupted_Protection;
|
||||
/* We expect a list close as next, so we can simply use strtoul()
|
||||
here. We might want to check that we only have digits - but this
|
||||
is nothing we should worry about */
|
||||
if (s[n] != ')' )
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s2kcount = strtoul (s, NULL, 10);
|
||||
if (!s2kcount)
|
||||
return GNUPG_Corrupted_Protection;
|
||||
s += n;
|
||||
s++; /* skip list end */
|
||||
|
||||
n = snext (&s);
|
||||
if (n != 16) /* Wrong blocksize for IV (we support ony aes-128) */
|
||||
return GNUPG_Corrupted_Protection;
|
||||
iv = s;
|
||||
s += n;
|
||||
if (*s != ')' )
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
|
||||
rc = do_decryption (s, n,
|
||||
passphrase, s2ksalt, s2kcount,
|
||||
iv, 16,
|
||||
&cleartext);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = merge_lists (protectedkey, prot_begin-protectedkey, cleartext,
|
||||
sha1hash, &final);
|
||||
xfree (cleartext);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = calculate_mic (final, sha1hash2);
|
||||
if (!rc && memcmp (sha1hash, sha1hash2, 20))
|
||||
rc = GNUPG_Corrupted_Protection;
|
||||
if (rc)
|
||||
{
|
||||
xfree (final);
|
||||
return rc;
|
||||
}
|
||||
|
||||
*result = final;
|
||||
*resultlen = gcry_sexp_canon_len (final, 0, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check the type of the private key, this is one of the constants:
|
||||
PRIVATE_KEY_UNKNOWN if we can't figure out the type (this is the
|
||||
value 0), PRIVATE_KEY_CLEAR for an unprotected private key.
|
||||
PRIVATE_KEY_PROTECTED for an protected private key or
|
||||
PRIVATE_KEY_SHADOWED for a sub key where the secret parts are stored
|
||||
elsewhere. */
|
||||
int
|
||||
agent_private_key_type (const unsigned char *privatekey)
|
||||
{
|
||||
const unsigned char *s;
|
||||
size_t n;
|
||||
|
||||
s = privatekey;
|
||||
if (*s != '(')
|
||||
return PRIVATE_KEY_UNKNOWN;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return PRIVATE_KEY_UNKNOWN;
|
||||
if (smatch (&s, n, "protected-private-key"))
|
||||
return PRIVATE_KEY_PROTECTED;
|
||||
if (smatch (&s, n, "shadowed-private-key"))
|
||||
return PRIVATE_KEY_SHADOWED;
|
||||
if (smatch (&s, n, "private-key"))
|
||||
return PRIVATE_KEY_CLEAR;
|
||||
return PRIVATE_KEY_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Transform a passphrase into a suitable key of length KEYLEN and
|
||||
store this key in the caller provided buffer KEY. The caller must
|
||||
provide an HASHALGO, a valid S2KMODE (see rfc-2440) and depending on
|
||||
that mode an S2KSALT of 8 random bytes and an S2KCOUNT (a suitable
|
||||
value is 96).
|
||||
|
||||
Returns an error code on failure. */
|
||||
static int
|
||||
hash_passphrase (const char *passphrase, int hashalgo,
|
||||
int s2kmode,
|
||||
const unsigned char *s2ksalt,
|
||||
unsigned long s2kcount,
|
||||
unsigned char *key, size_t keylen)
|
||||
{
|
||||
GCRY_MD_HD md;
|
||||
int pass, i;
|
||||
int used = 0;
|
||||
int pwlen = strlen (passphrase);
|
||||
|
||||
if ( (s2kmode != 0 && s2kmode != 1 && s2kmode != 3)
|
||||
|| !hashalgo || !keylen || !key || !passphrase)
|
||||
return GNUPG_Invalid_Value;
|
||||
if ((s2kmode == 1 ||s2kmode == 3) && !s2ksalt)
|
||||
return GNUPG_Invalid_Value;
|
||||
|
||||
md = gcry_md_open (hashalgo, GCRY_MD_FLAG_SECURE);
|
||||
if (!md)
|
||||
return map_gcry_err (gcry_errno());
|
||||
|
||||
for (pass=0; used < keylen; pass++)
|
||||
{
|
||||
if (pass)
|
||||
{
|
||||
gcry_md_reset (md);
|
||||
for (i=0; i < pass; i++) /* preset the hash context */
|
||||
gcry_md_putc (md, 0);
|
||||
}
|
||||
|
||||
if (s2kmode == 1 || s2kmode == 3)
|
||||
{
|
||||
int len2 = pwlen + 8;
|
||||
unsigned long count = len2;
|
||||
|
||||
if (s2kmode == 3)
|
||||
{
|
||||
count = (16ul + (s2kcount & 15)) << ((s2kcount >> 4) + 6);
|
||||
if (count < len2)
|
||||
count = len2;
|
||||
}
|
||||
|
||||
while (count > len2)
|
||||
{
|
||||
gcry_md_write (md, s2ksalt, 8);
|
||||
gcry_md_write (md, passphrase, pwlen);
|
||||
count -= len2;
|
||||
}
|
||||
if (count < 8)
|
||||
gcry_md_write (md, s2ksalt, count);
|
||||
else
|
||||
{
|
||||
gcry_md_write (md, s2ksalt, 8);
|
||||
count -= 8;
|
||||
gcry_md_write (md, passphrase, count);
|
||||
}
|
||||
}
|
||||
else
|
||||
gcry_md_write (md, passphrase, pwlen);
|
||||
|
||||
gcry_md_final (md);
|
||||
i = gcry_md_get_algo_dlen (hashalgo);
|
||||
if (i > keylen - used)
|
||||
i = keylen - used;
|
||||
memcpy (key+used, gcry_md_read (md, hashalgo), i);
|
||||
used += i;
|
||||
}
|
||||
gcry_md_close(md);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Create a shadow key from a public key. We use the shadow protocol
|
||||
"ti-v1" and insert the S-expressionn SHADOW_INFO. The resulting
|
||||
S-expression is returned in an allocated buffer RESULT will point
|
||||
to. The input parameters are expected to be valid canonilized
|
||||
S-expressions */
|
||||
int
|
||||
agent_shadow_key (const unsigned char *pubkey,
|
||||
const unsigned char *shadow_info,
|
||||
unsigned char **result)
|
||||
{
|
||||
const unsigned char *s;
|
||||
const unsigned char *point;
|
||||
size_t n;
|
||||
int depth = 0;
|
||||
unsigned char *p;
|
||||
size_t pubkey_len = gcry_sexp_canon_len (pubkey, 0, NULL,NULL);
|
||||
size_t shadow_info_len = gcry_sexp_canon_len (shadow_info, 0, NULL,NULL);
|
||||
|
||||
if (!pubkey_len || !shadow_info_len)
|
||||
return GNUPG_Invalid_Value;
|
||||
s = pubkey;
|
||||
if (*s != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
depth++;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (!smatch (&s, n, "public-key"))
|
||||
return GNUPG_Unknown_Sexp;
|
||||
if (*s != '(')
|
||||
return GNUPG_Unknown_Sexp;
|
||||
depth++;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s += n; /* skip over the algorithm name */
|
||||
|
||||
while (*s != ')')
|
||||
{
|
||||
if (*s != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
depth++;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s += n;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s +=n; /* skip value */
|
||||
if (*s != ')')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
depth--;
|
||||
s++;
|
||||
}
|
||||
point = s; /* insert right before the point */
|
||||
depth--;
|
||||
s++;
|
||||
assert (depth == 1);
|
||||
|
||||
/* calculate required length by taking in account: the "shadowed-"
|
||||
prefix, the "shadowed", "t1-v1" as well as some parenthesis */
|
||||
n = 12 + pubkey_len + 1 + 3+8 + 2+5 + shadow_info_len + 1;
|
||||
*result = p = xtrymalloc (n);
|
||||
if (!p)
|
||||
return GNUPG_Out_Of_Core;
|
||||
p = stpcpy (p, "(20:shadowed-private-key");
|
||||
/* (10:public-key ...)*/
|
||||
memcpy (p, pubkey+14, point - (pubkey+14));
|
||||
p += point - (pubkey+14);
|
||||
p = stpcpy (p, "(8:shadowed5:t1-v1");
|
||||
memcpy (p, shadow_info, shadow_info_len);
|
||||
p += shadow_info_len;
|
||||
*p++ = ')';
|
||||
memcpy (p, point, pubkey_len - (point - pubkey));
|
||||
p += pubkey_len - (point - pubkey);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse a canonical encoded shadowed key and return a pointer to the
|
||||
inner list with the shadow_info */
|
||||
int
|
||||
agent_get_shadow_info (const unsigned char *shadowkey,
|
||||
unsigned char const **shadow_info)
|
||||
{
|
||||
const unsigned char *s;
|
||||
size_t n;
|
||||
int depth = 0;
|
||||
|
||||
s = shadowkey;
|
||||
if (*s != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
depth++;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (!smatch (&s, n, "shadowed-private-key"))
|
||||
return GNUPG_Unknown_Sexp;
|
||||
if (*s != '(')
|
||||
return GNUPG_Unknown_Sexp;
|
||||
depth++;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s += n; /* skip over the algorithm name */
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (*s == ')')
|
||||
return GNUPG_Unknown_Sexp;
|
||||
if (*s != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
depth++;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (smatch (&s, n, "shadowed"))
|
||||
break;
|
||||
s += n;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s +=n; /* skip value */
|
||||
if (*s != ')')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
depth--;
|
||||
s++;
|
||||
}
|
||||
/* found the shadowed list, s points to the protocol */
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (smatch (&s, n, "t1-v1"))
|
||||
{
|
||||
if (*s != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
*shadow_info = s;
|
||||
}
|
||||
else
|
||||
return GNUPG_Unsupported_Protocol;
|
||||
return 0;
|
||||
}
|
||||
|
460
agent/query.c
460
agent/query.c
|
@ -1,460 +0,0 @@
|
|||
/* query.c - fork of the pinentry to query stuff from the user
|
||||
* Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#ifdef USE_GNU_PTH
|
||||
# include <pth.h>
|
||||
#endif
|
||||
|
||||
#include "agent.h"
|
||||
#include "i18n.h"
|
||||
#include "../assuan/assuan.h"
|
||||
|
||||
#ifdef _POSIX_OPEN_MAX
|
||||
#define MAX_OPEN_FDS _POSIX_OPEN_MAX
|
||||
#else
|
||||
#define MAX_OPEN_FDS 20
|
||||
#endif
|
||||
|
||||
static ASSUAN_CONTEXT entry_ctx = NULL;
|
||||
#ifdef USE_GNU_PTH
|
||||
static pth_mutex_t entry_lock = PTH_MUTEX_INIT;
|
||||
#endif
|
||||
|
||||
/* data to be passed to our callbacks */
|
||||
struct entry_parm_s {
|
||||
int lines;
|
||||
size_t size;
|
||||
char *buffer;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
static int
|
||||
unlock_pinentry (int rc)
|
||||
{
|
||||
#ifdef USE_GNU_PTH
|
||||
if (!pth_mutex_release (&entry_lock))
|
||||
{
|
||||
log_error ("failed to release the entry lock\n");
|
||||
if (!rc)
|
||||
rc = GNUPG_Internal_Error;
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Fork off the pin entry if this has not already been done. Note,
|
||||
that this function must always be used to aquire the lock for the
|
||||
pinentry - we will serialize _all_ pinentry calls.
|
||||
*/
|
||||
static int
|
||||
start_pinentry (void)
|
||||
{
|
||||
int rc;
|
||||
const char *pgmname;
|
||||
ASSUAN_CONTEXT ctx;
|
||||
const char *argv[5];
|
||||
int no_close_list[3];
|
||||
int i;
|
||||
|
||||
#ifdef USE_GNU_PTH
|
||||
if (!pth_mutex_acquire (&entry_lock, 0, NULL))
|
||||
{
|
||||
log_error ("failed to acquire the entry lock\n");
|
||||
return GNUPG_Internal_Error;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (entry_ctx)
|
||||
return 0;
|
||||
|
||||
if (opt.verbose)
|
||||
log_info ("no running PIN Entry - starting it\n");
|
||||
|
||||
if (fflush (NULL))
|
||||
{
|
||||
log_error ("error flushing pending output: %s\n", strerror (errno));
|
||||
return unlock_pinentry (seterr (Write_Error));
|
||||
}
|
||||
|
||||
if (!opt.pinentry_program || !*opt.pinentry_program)
|
||||
opt.pinentry_program = GNUPG_DEFAULT_PINENTRY;
|
||||
if ( !(pgmname = strrchr (opt.pinentry_program, '/')))
|
||||
pgmname = opt.pinentry_program;
|
||||
else
|
||||
pgmname++;
|
||||
|
||||
/* FIXME: We must do this thread specific */
|
||||
argv[0] = pgmname;
|
||||
if (opt.display && !opt.keep_display)
|
||||
{
|
||||
argv[1] = "--display";
|
||||
argv[2] = opt.display;
|
||||
argv[3] = NULL;
|
||||
}
|
||||
else
|
||||
argv[1] = NULL;
|
||||
|
||||
i=0;
|
||||
if (!opt.running_detached)
|
||||
{
|
||||
if (log_get_fd () != -1)
|
||||
no_close_list[i++] = log_get_fd ();
|
||||
no_close_list[i++] = fileno (stderr);
|
||||
}
|
||||
no_close_list[i] = -1;
|
||||
|
||||
/* connect to the pinentry and perform initial handshaking */
|
||||
rc = assuan_pipe_connect (&ctx, opt.pinentry_program, (char**)argv,
|
||||
no_close_list);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("can't connect to the PIN entry module: %s\n",
|
||||
assuan_strerror (rc));
|
||||
return unlock_pinentry (seterr (No_PIN_Entry));
|
||||
}
|
||||
entry_ctx = ctx;
|
||||
|
||||
if (DBG_ASSUAN)
|
||||
log_debug ("connection to PIN entry established\n");
|
||||
|
||||
rc = assuan_transact (entry_ctx,
|
||||
opt.no_grab? "OPTION no-grab":"OPTION grab",
|
||||
NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
if (opt.ttyname && !opt.keep_tty)
|
||||
{
|
||||
char *optstr;
|
||||
if (asprintf (&optstr, "OPTION ttyname=%s", opt.ttyname) < 0 )
|
||||
return unlock_pinentry (GNUPG_Out_Of_Core);
|
||||
rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL);
|
||||
free (optstr);
|
||||
if (rc)
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
}
|
||||
if (opt.ttytype && !opt.keep_tty)
|
||||
{
|
||||
char *optstr;
|
||||
if (asprintf (&optstr, "OPTION ttytype=%s", opt.ttytype) < 0 )
|
||||
return unlock_pinentry (GNUPG_Out_Of_Core);
|
||||
rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL);
|
||||
if (rc)
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
}
|
||||
if (opt.lc_ctype)
|
||||
{
|
||||
char *optstr;
|
||||
if (asprintf (&optstr, "OPTION lc-ctype=%s", opt.lc_ctype) < 0 )
|
||||
return unlock_pinentry (GNUPG_Out_Of_Core);
|
||||
rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL);
|
||||
if (rc)
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
}
|
||||
if (opt.lc_messages)
|
||||
{
|
||||
char *optstr;
|
||||
if (asprintf (&optstr, "OPTION lc-messages=%s", opt.lc_messages) < 0 )
|
||||
return unlock_pinentry (GNUPG_Out_Of_Core);
|
||||
rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL);
|
||||
if (rc)
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static AssuanError
|
||||
getpin_cb (void *opaque, const void *buffer, size_t length)
|
||||
{
|
||||
struct entry_parm_s *parm = opaque;
|
||||
|
||||
if (!buffer)
|
||||
return 0;
|
||||
|
||||
/* we expect the pin to fit on one line */
|
||||
if (parm->lines || length >= parm->size)
|
||||
return ASSUAN_Too_Much_Data;
|
||||
|
||||
/* fixme: we should make sure that the assuan buffer is allocated in
|
||||
secure memory or read the response byte by byte */
|
||||
memcpy (parm->buffer, buffer, length);
|
||||
parm->buffer[length] = 0;
|
||||
parm->lines++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
all_digitsp( const char *s)
|
||||
{
|
||||
for (; *s && *s >= '0' && *s <= '9'; s++)
|
||||
;
|
||||
return !*s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Call the Entry and ask for the PIN. We do check for a valid PIN
|
||||
number here and repeat it as long as we have invalid formed
|
||||
numbers. */
|
||||
int
|
||||
agent_askpin (const char *desc_text, struct pin_entry_info_s *pininfo)
|
||||
{
|
||||
int rc;
|
||||
char line[ASSUAN_LINELENGTH];
|
||||
struct entry_parm_s parm;
|
||||
const char *errtext = NULL;
|
||||
int is_pin = 0;
|
||||
|
||||
if (opt.batch)
|
||||
return 0; /* fixme: we should return BAD PIN */
|
||||
|
||||
if (!pininfo || pininfo->max_length < 1)
|
||||
return seterr (Invalid_Value);
|
||||
if (!desc_text && pininfo->min_digits)
|
||||
desc_text = _("Please enter your PIN, so that the secret key "
|
||||
"can be unlocked for this session");
|
||||
else if (!desc_text)
|
||||
desc_text = _("Please enter your passphrase, so that the secret key "
|
||||
"can be unlocked for this session");
|
||||
|
||||
is_pin = desc_text && strstr (desc_text, "PIN");
|
||||
|
||||
rc = start_pinentry ();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
snprintf (line, DIM(line)-1, "SETDESC %s", desc_text);
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
|
||||
rc = assuan_transact (entry_ctx,
|
||||
is_pin? "SETPROMPT PIN:"
|
||||
: "SETPROMPT Passphrase:",
|
||||
NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
|
||||
for (;pininfo->failed_tries < pininfo->max_tries; pininfo->failed_tries++)
|
||||
{
|
||||
memset (&parm, 0, sizeof parm);
|
||||
parm.size = pininfo->max_length;
|
||||
parm.buffer = pininfo->pin;
|
||||
|
||||
if (errtext)
|
||||
{
|
||||
/* fixme: should we show the try count? It must be translated */
|
||||
snprintf (line, DIM(line)-1, "SETERROR %s (try %d of %d)",
|
||||
errtext, pininfo->failed_tries+1, pininfo->max_tries);
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
errtext = NULL;
|
||||
}
|
||||
|
||||
rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm,
|
||||
NULL, NULL, NULL, NULL);
|
||||
if (rc == ASSUAN_Too_Much_Data)
|
||||
errtext = is_pin? _("PIN too long")
|
||||
: _("Passphrase too long");
|
||||
else if (rc)
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
|
||||
if (!errtext && pininfo->min_digits)
|
||||
{
|
||||
/* do some basic checks on the entered PIN. */
|
||||
if (!all_digitsp (pininfo->pin))
|
||||
errtext = _("Invalid characters in PIN");
|
||||
else if (pininfo->max_digits
|
||||
&& strlen (pininfo->pin) > pininfo->max_digits)
|
||||
errtext = _("PIN too long");
|
||||
else if (strlen (pininfo->pin) < pininfo->min_digits)
|
||||
errtext = _("PIN too short");
|
||||
}
|
||||
|
||||
if (!errtext && pininfo->check_cb)
|
||||
{
|
||||
/* More checks by utilizing the optional callback. */
|
||||
pininfo->cb_errtext = NULL;
|
||||
rc = pininfo->check_cb (pininfo);
|
||||
if (rc == -1 && pininfo->cb_errtext)
|
||||
errtext = pininfo->cb_errtext;
|
||||
else if (rc == GNUPG_Bad_Passphrase || rc == GNUPG_Bad_PIN)
|
||||
errtext = (is_pin? _("Bad PIN")
|
||||
: _("Bad Passphrase"));
|
||||
else if (rc)
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
}
|
||||
|
||||
if (!errtext)
|
||||
return unlock_pinentry (0); /* okay, got a PIN or passphrase */
|
||||
}
|
||||
|
||||
return unlock_pinentry (pininfo->min_digits? GNUPG_Bad_PIN
|
||||
: GNUPG_Bad_Passphrase);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Ask for the passphrase using the supplied arguments. The
|
||||
passphrase is returned in RETPASS as an hex encoded string to be
|
||||
freed by the caller */
|
||||
int
|
||||
agent_get_passphrase (char **retpass, const char *desc, const char *prompt,
|
||||
const char *errtext)
|
||||
{
|
||||
|
||||
int rc;
|
||||
char line[ASSUAN_LINELENGTH];
|
||||
struct entry_parm_s parm;
|
||||
unsigned char *p, *hexstring;
|
||||
int i;
|
||||
|
||||
*retpass = NULL;
|
||||
if (opt.batch)
|
||||
return GNUPG_Bad_Passphrase;
|
||||
|
||||
rc = start_pinentry ();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!prompt)
|
||||
prompt = desc && strstr (desc, "PIN")? "PIN": _("Passphrase");
|
||||
|
||||
|
||||
if (desc)
|
||||
snprintf (line, DIM(line)-1, "SETDESC %s", desc);
|
||||
else
|
||||
snprintf (line, DIM(line)-1, "RESET");
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
|
||||
snprintf (line, DIM(line)-1, "SETPROMPT %s", prompt);
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
|
||||
if (errtext)
|
||||
{
|
||||
snprintf (line, DIM(line)-1, "SETERROR %s", errtext);
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
}
|
||||
|
||||
memset (&parm, 0, sizeof parm);
|
||||
parm.size = ASSUAN_LINELENGTH/2 - 5;
|
||||
parm.buffer = gcry_malloc_secure (parm.size+10);
|
||||
if (!parm.buffer)
|
||||
return unlock_pinentry (seterr (Out_Of_Core));
|
||||
|
||||
assuan_begin_confidential (entry_ctx);
|
||||
rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
{
|
||||
xfree (parm.buffer);
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
}
|
||||
|
||||
hexstring = gcry_malloc_secure (strlen (parm.buffer)*2+1);
|
||||
if (!hexstring)
|
||||
{
|
||||
xfree (parm.buffer);
|
||||
return unlock_pinentry (seterr (Out_Of_Core));
|
||||
}
|
||||
|
||||
for (i=0, p=parm.buffer; *p; p++, i += 2)
|
||||
sprintf (hexstring+i, "%02X", *p);
|
||||
|
||||
xfree (parm.buffer);
|
||||
*retpass = hexstring;
|
||||
return unlock_pinentry (0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Pop up the PIN-entry, display the text and the prompt and ask the
|
||||
user to confirm this. We return 0 for success, ie. the used
|
||||
confirmed it, GNUPG_Not_Confirmed for what the text says or an
|
||||
other error. */
|
||||
int
|
||||
agent_get_confirmation (const char *desc, const char *ok, const char *cancel)
|
||||
{
|
||||
int rc;
|
||||
char line[ASSUAN_LINELENGTH];
|
||||
|
||||
rc = start_pinentry ();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (desc)
|
||||
snprintf (line, DIM(line)-1, "SETDESC %s", desc);
|
||||
else
|
||||
snprintf (line, DIM(line)-1, "RESET");
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
|
||||
if (ok)
|
||||
{
|
||||
snprintf (line, DIM(line)-1, "SETOK %s", ok);
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
}
|
||||
if (cancel)
|
||||
{
|
||||
snprintf (line, DIM(line)-1, "SETCANCEL %s", cancel);
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
}
|
||||
|
||||
rc = assuan_transact (entry_ctx, "CONFIRM", NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
return unlock_pinentry (map_assuan_err (rc));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
/* sexp-parse.h - S-Exp helper functions
|
||||
* Copyright (C) 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#ifndef SEXP_PARSE_H
|
||||
#define SEXP_PARSE_H
|
||||
|
||||
#include "../common/util.h"
|
||||
|
||||
/* Return the length of the next S-Exp part and update the pointer to
|
||||
the first data byte. 0 is return on error */
|
||||
static inline size_t
|
||||
snext (unsigned char const **buf)
|
||||
{
|
||||
const unsigned char *s;
|
||||
int n;
|
||||
|
||||
s = *buf;
|
||||
for (n=0; *s && *s != ':' && digitp (s); s++)
|
||||
n = n*10 + atoi_1 (s);
|
||||
if (!n || *s != ':')
|
||||
return 0; /* we don't allow empty lengths */
|
||||
*buf = s+1;
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Skip over the S-Expression BUF points to and update BUF to point to
|
||||
the chacter right behind. DEPTH gives the initial number of open
|
||||
lists and may be passed as a positive number to skip over the
|
||||
remainder of an S-Expression if the current position is somewhere
|
||||
in an S-Expression. The function may return an error code if it
|
||||
encounters an impossible conditions */
|
||||
static inline int
|
||||
sskip (unsigned char const **buf, int *depth)
|
||||
{
|
||||
const unsigned char *s = *buf;
|
||||
size_t n;
|
||||
int d = *depth;
|
||||
|
||||
while (d > 0)
|
||||
{
|
||||
if (*s == '(')
|
||||
{
|
||||
d++;
|
||||
s++;
|
||||
}
|
||||
else if (*s == ')')
|
||||
{
|
||||
d--;
|
||||
s++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!d)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s += n;
|
||||
}
|
||||
}
|
||||
*buf = s;
|
||||
*depth = d;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Check whether the the string at the address BUF points to matches
|
||||
the token. Return true on match and update BUF to point behind the
|
||||
token. */
|
||||
static inline int
|
||||
smatch (unsigned char const **buf, size_t buflen, const char *token)
|
||||
{
|
||||
size_t toklen = strlen (token);
|
||||
|
||||
if (buflen != toklen || memcmp (*buf, token, toklen))
|
||||
return 0;
|
||||
*buf += toklen;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /*SEXP_PARSE_H*/
|
|
@ -1,485 +0,0 @@
|
|||
/* simple-pwquery.c - A simple password query cleint for gpg-agent
|
||||
* Copyright (C) 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
/* This module is intended as a standalone client implementation to
|
||||
gpg-agent's GET_PASSPHRASE command. In particular it does not use
|
||||
the Assuan library and can only cope with an already running
|
||||
gpg-agent. Some stuff is configurable in the header file. */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#ifdef HAVE_LOCALE_H
|
||||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
#define SIMPLE_PWQUERY_IMPLEMENTATION 1
|
||||
#include "simple-pwquery.h"
|
||||
|
||||
#if defined(SPWQ_USE_LOGGING) && !defined(HAVE_JNLIB_LOGGING)
|
||||
# undef SPWQ_USE_LOGGING
|
||||
#endif
|
||||
|
||||
#ifndef _
|
||||
#define _(a) (a)
|
||||
#endif
|
||||
|
||||
#if !defined (hexdigitp) && !defined (xtoi_2)
|
||||
#define digitp(p) (*(p) >= '0' && *(p) <= '9')
|
||||
#define hexdigitp(a) (digitp (a) \
|
||||
|| (*(a) >= 'A' && *(a) <= 'F') \
|
||||
|| (*(a) >= 'a' && *(a) <= 'f'))
|
||||
#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
|
||||
*(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
|
||||
#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
|
||||
#endif
|
||||
|
||||
|
||||
/* Write NBYTES of BUF to file descriptor FD. */
|
||||
static int
|
||||
writen (int fd, const void *buf, size_t nbytes)
|
||||
{
|
||||
size_t nleft = nbytes;
|
||||
int nwritten;
|
||||
|
||||
while (nleft > 0)
|
||||
{
|
||||
nwritten = write( fd, buf, nleft );
|
||||
if (nwritten < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
nwritten = 0;
|
||||
else {
|
||||
#ifdef SPWQ_USE_LOGGING
|
||||
log_error ("write failed: %s\n", strerror (errno));
|
||||
#endif
|
||||
return SPWQ_IO_ERROR;
|
||||
}
|
||||
}
|
||||
nleft -= nwritten;
|
||||
buf = (const char*)buf + nwritten;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Read an entire line and return number of bytes read. */
|
||||
static int
|
||||
readline (int fd, char *buf, size_t buflen)
|
||||
{
|
||||
size_t nleft = buflen;
|
||||
char *p;
|
||||
int nread = 0;
|
||||
|
||||
while (nleft > 0)
|
||||
{
|
||||
int n = read (fd, buf, nleft);
|
||||
if (n < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return -(SPWQ_IO_ERROR);
|
||||
}
|
||||
else if (!n)
|
||||
{
|
||||
return -(SPWQ_PROTOCOL_ERROR); /* incomplete line */
|
||||
}
|
||||
p = buf;
|
||||
nleft -= n;
|
||||
buf += n;
|
||||
nread += n;
|
||||
|
||||
for (; n && *p != '\n'; n--, p++)
|
||||
;
|
||||
if (n)
|
||||
{
|
||||
break; /* at least one full line available - that's enough.
|
||||
This function is just a simple implementation, so
|
||||
it is okay to forget about pending bytes */
|
||||
}
|
||||
}
|
||||
|
||||
return nread;
|
||||
}
|
||||
|
||||
|
||||
/* Send an option to the agent */
|
||||
static int
|
||||
agent_send_option (int fd, const char *name, const char *value)
|
||||
{
|
||||
char buf[200];
|
||||
int nread;
|
||||
char *line;
|
||||
int i;
|
||||
|
||||
line = spwq_malloc (7 + strlen (name) + 1 + strlen (value) + 2);
|
||||
if (!line)
|
||||
return SPWQ_OUT_OF_CORE;
|
||||
strcpy (stpcpy (stpcpy (stpcpy (
|
||||
stpcpy (line, "OPTION "), name), "="), value), "\n");
|
||||
i = writen (fd, line, strlen (line));
|
||||
spwq_free (line);
|
||||
if (i)
|
||||
return i;
|
||||
|
||||
/* get response */
|
||||
nread = readline (fd, buf, DIM(buf)-1);
|
||||
if (nread < 0)
|
||||
return -nread;
|
||||
if (nread < 3)
|
||||
return SPWQ_PROTOCOL_ERROR;
|
||||
|
||||
if (buf[0] == 'O' && buf[1] == 'K' && (buf[2] == ' ' || buf[2] == '\n'))
|
||||
return 0; /* okay */
|
||||
|
||||
return SPWQ_ERR_RESPONSE;
|
||||
}
|
||||
|
||||
|
||||
/* Send all available options to the agent. */
|
||||
static int
|
||||
agent_send_all_options (int fd)
|
||||
{
|
||||
char *dft_display = NULL;
|
||||
char *dft_ttyname = NULL;
|
||||
char *dft_ttytype = NULL;
|
||||
int rc = 0;
|
||||
|
||||
dft_display = getenv ("DISPLAY");
|
||||
if (dft_display)
|
||||
{
|
||||
if ((rc = agent_send_option (fd, "display", dft_display)))
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (ttyname (1))
|
||||
dft_ttyname = ttyname (1);
|
||||
if (dft_ttyname)
|
||||
{
|
||||
if ((rc=agent_send_option (fd, "ttyname", dft_ttyname)))
|
||||
return rc;
|
||||
}
|
||||
|
||||
dft_ttytype = getenv ("TERM");
|
||||
if (dft_ttyname && dft_ttytype)
|
||||
{
|
||||
if ((rc = agent_send_option (fd, "ttytype", dft_ttytype)))
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if defined(HAVE_SETLOCALE)
|
||||
{
|
||||
char *old_lc = NULL;
|
||||
char *dft_lc = NULL;
|
||||
|
||||
#if defined(LC_CTYPE)
|
||||
old_lc = setlocale (LC_CTYPE, NULL);
|
||||
if (old_lc)
|
||||
{
|
||||
char *p = spwq_malloc (strlen (old_lc)+1);
|
||||
if (!p)
|
||||
return SPWQ_OUT_OF_CORE;
|
||||
strcpy (p, old_lc);
|
||||
old_lc = p;
|
||||
}
|
||||
dft_lc = setlocale (LC_CTYPE, "");
|
||||
if (dft_ttyname && dft_lc)
|
||||
rc = agent_send_option (fd, "lc-ctype", dft_lc);
|
||||
if (old_lc)
|
||||
{
|
||||
setlocale (LC_CTYPE, old_lc);
|
||||
spwq_free (old_lc);
|
||||
}
|
||||
if (rc)
|
||||
return rc;
|
||||
#endif
|
||||
|
||||
#if defined(LC_MESSAGES)
|
||||
old_lc = setlocale (LC_MESSAGES, NULL);
|
||||
if (old_lc)
|
||||
{
|
||||
char *p = spwq_malloc (strlen (old_lc)+1);
|
||||
if (!p)
|
||||
return SPWQ_OUT_OF_CORE;
|
||||
strcpy (p, old_lc);
|
||||
old_lc = p;
|
||||
}
|
||||
dft_lc = setlocale (LC_MESSAGES, "");
|
||||
if (dft_ttyname && dft_lc)
|
||||
rc = agent_send_option (fd, "lc-messages", dft_lc);
|
||||
if (old_lc)
|
||||
{
|
||||
setlocale (LC_MESSAGES, old_lc);
|
||||
spwq_free (old_lc);
|
||||
}
|
||||
if (rc)
|
||||
return rc;
|
||||
#endif
|
||||
}
|
||||
#endif /*HAVE_SETLOCALE*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Try to open a connection to the agent, send all options and return
|
||||
the file descriptor for the connection. Return -1 in case of
|
||||
error. */
|
||||
static int
|
||||
agent_open (int *rfd)
|
||||
{
|
||||
int rc;
|
||||
int fd;
|
||||
char *infostr, *p;
|
||||
struct sockaddr_un client_addr;
|
||||
size_t len;
|
||||
int prot;
|
||||
char line[200];
|
||||
int nread;
|
||||
|
||||
*rfd = -1;
|
||||
infostr = getenv ( "GPG_AGENT_INFO" );
|
||||
if ( !infostr )
|
||||
{
|
||||
#ifdef SPWQ_USE_LOGGING
|
||||
log_error (_("gpg-agent is not available in this session\n"));
|
||||
#endif
|
||||
return SPWQ_NO_AGENT;
|
||||
}
|
||||
|
||||
if ( !(p = strchr ( infostr, ':')) || p == infostr
|
||||
|| (p-infostr)+1 >= sizeof client_addr.sun_path )
|
||||
{
|
||||
#ifdef SPWQ_USE_LOGGING
|
||||
log_error ( _("malformed GPG_AGENT_INFO environment variable\n"));
|
||||
#endif
|
||||
return SPWQ_NO_AGENT;
|
||||
}
|
||||
*p++ = 0;
|
||||
|
||||
while (*p && *p != ':')
|
||||
p++;
|
||||
prot = *p? atoi (p+1) : 0;
|
||||
if ( prot != 1)
|
||||
{
|
||||
#ifdef SPWQ_USE_LOGGING
|
||||
log_error (_("gpg-agent protocol version %d is not supported\n"),prot);
|
||||
#endif
|
||||
return SPWQ_PROTOCOL_ERROR;
|
||||
}
|
||||
|
||||
if( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 )
|
||||
{
|
||||
#ifdef SPWQ_USE_LOGGING
|
||||
log_error ("can't create socket: %s\n", strerror(errno) );
|
||||
#endif
|
||||
return SPWQ_SYS_ERROR;
|
||||
}
|
||||
|
||||
memset (&client_addr, 0, sizeof client_addr);
|
||||
client_addr.sun_family = AF_UNIX;
|
||||
strcpy (client_addr.sun_path, infostr);
|
||||
len = (offsetof (struct sockaddr_un, sun_path)
|
||||
+ strlen(client_addr.sun_path) + 1);
|
||||
|
||||
if (connect (fd, (struct sockaddr*)&client_addr, len ) == -1)
|
||||
{
|
||||
#ifdef SPWQ_USE_LOGGING
|
||||
log_error ( _("can't connect to `%s': %s\n"), infostr, strerror (errno));
|
||||
#endif
|
||||
close (fd );
|
||||
return SPWQ_IO_ERROR;
|
||||
}
|
||||
|
||||
nread = readline (fd, line, DIM(line));
|
||||
if (nread < 3 || !(line[0] == 'O' && line[1] == 'K'
|
||||
&& (line[2] == '\n' || line[2] == ' ')) )
|
||||
{
|
||||
#ifdef SPWQ_USE_LOGGING
|
||||
log_error ( _("communication problem with gpg-agent\n"));
|
||||
#endif
|
||||
close (fd );
|
||||
return SPWQ_PROTOCOL_ERROR;
|
||||
}
|
||||
|
||||
rc = agent_send_all_options (fd);
|
||||
if (rc)
|
||||
{
|
||||
#ifdef SPWQ_USE_LOGGING
|
||||
log_error (_("problem setting the gpg-agent options\n"));
|
||||
#endif
|
||||
close (fd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
*rfd = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Copy text to BUFFER and escape as required. Return a poiinter to
|
||||
the end of the new buffer. NOte that BUFFER must be large enough
|
||||
to keep the entire text; allocataing it 3 times the size of TEXT
|
||||
is sufficient. */
|
||||
static char *
|
||||
copy_and_escape (char *buffer, const char *text)
|
||||
{
|
||||
int i;
|
||||
char *p = buffer;
|
||||
|
||||
for (i=0; text[i]; i++)
|
||||
{
|
||||
if (text[i] < ' ' || text[i] == '+')
|
||||
{
|
||||
sprintf (p, "%%%02X", text[i]);
|
||||
p += 3;
|
||||
}
|
||||
else if (text[i] == ' ')
|
||||
*p++ = '+';
|
||||
else
|
||||
*p++ = text[i];
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/* Ask the gpg-agent for a passphrase and present the user with a
|
||||
DESCRIPTION, a PROMPT and optiaonlly with a TRYAGAIN extra text.
|
||||
If a CACHEID is not NULL it is used to locate the passphrase in in
|
||||
the cache and store it under this ID. If ERRORCODE is not NULL it
|
||||
should point a variable receiving an errorcode; thsi errocode might
|
||||
be 0 if the user canceled the operation. The function returns NULL
|
||||
to indicate an error. */
|
||||
char *
|
||||
simple_pwquery (const char *cacheid,
|
||||
const char *tryagain,
|
||||
const char *prompt,
|
||||
const char *description,
|
||||
int *errorcode)
|
||||
{
|
||||
int fd = -1;
|
||||
int nread;
|
||||
char *result = NULL;
|
||||
char *pw = NULL;
|
||||
char *p;
|
||||
int rc, i;
|
||||
|
||||
rc = agent_open (&fd);
|
||||
if (rc)
|
||||
goto leave;
|
||||
|
||||
if (!cacheid)
|
||||
cacheid = "X";
|
||||
if (!tryagain)
|
||||
tryagain = "X";
|
||||
if (!prompt)
|
||||
prompt = "X";
|
||||
if (!description)
|
||||
description = "X";
|
||||
|
||||
{
|
||||
char *line;
|
||||
/* We allocate 3 times the needed space so that there is enough
|
||||
space for escaping. */
|
||||
line = spwq_malloc (15
|
||||
+ 3*strlen (cacheid) + 1
|
||||
+ 3*strlen (tryagain) + 1
|
||||
+ 3*strlen (prompt) + 1
|
||||
+ 3*strlen (description) + 1
|
||||
+ 2);
|
||||
if (!line)
|
||||
{
|
||||
rc = SPWQ_OUT_OF_CORE;
|
||||
goto leave;
|
||||
}
|
||||
strcpy (line, "GET_PASSPHRASE ");
|
||||
p = line+15;
|
||||
p = copy_and_escape (p, cacheid);
|
||||
*p++ = ' ';
|
||||
p = copy_and_escape (p, tryagain);
|
||||
*p++ = ' ';
|
||||
p = copy_and_escape (p, prompt);
|
||||
*p++ = ' ';
|
||||
p = copy_and_escape (p, description);
|
||||
*p++ = '\n';
|
||||
rc = writen (fd, line, p - line);
|
||||
spwq_free (line);
|
||||
if (rc)
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* get response */
|
||||
pw = spwq_secure_malloc (500);
|
||||
nread = readline (fd, pw, 499);
|
||||
if (nread < 0)
|
||||
{
|
||||
rc = -nread;
|
||||
goto leave;
|
||||
}
|
||||
if (nread < 3)
|
||||
{
|
||||
rc = SPWQ_PROTOCOL_ERROR;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (pw[0] == 'O' && pw[1] == 'K' && pw[2] == ' ')
|
||||
{ /* we got a passphrase - convert it back from hex */
|
||||
size_t pwlen = 0;
|
||||
|
||||
for (i=3; i < nread && hexdigitp (pw+i); i+=2)
|
||||
pw[pwlen++] = xtoi_2 (pw+i);
|
||||
pw[pwlen] = 0; /* make a C String */
|
||||
result = pw;
|
||||
pw = NULL;
|
||||
}
|
||||
else if (nread > 7 && !memcmp (pw, "ERR 111", 7)
|
||||
&& (pw[7] == ' ' || pw[7] == '\n') )
|
||||
{
|
||||
#ifdef SPWQ_USE_LOGGING
|
||||
log_info (_("canceled by user\n") );
|
||||
#endif
|
||||
*errorcode = 0; /* canceled */
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef SPWQ_USE_LOGGING
|
||||
log_error (_("problem with the agent\n"));
|
||||
#endif
|
||||
rc = SPWQ_ERR_RESPONSE;
|
||||
}
|
||||
|
||||
leave:
|
||||
if (errorcode)
|
||||
*errorcode = rc;
|
||||
if (fd != -1)
|
||||
close (fd);
|
||||
if (pw)
|
||||
spwq_free (pw);
|
||||
return result;
|
||||
}
|
|
@ -1,304 +0,0 @@
|
|||
/* trustlist.c - Maintain the list of trusted keys
|
||||
* Copyright (C) 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "agent.h"
|
||||
#include "../assuan/assuan.h" /* fixme: need a way to avoid assuan
|
||||
calls here */
|
||||
|
||||
static const char headerblurb[] =
|
||||
"# This is the list of trusted keys. Comments like this one and empty\n"
|
||||
"# lines are allowed but keep in mind that the entire file is integrity\n"
|
||||
"# protected by the use of a MAC, so changing the file does not make\n"
|
||||
"# much sense without the knowledge of the MAC key. Lines do have a\n"
|
||||
"# length limit but this is not serious limitation as the format of the\n"
|
||||
"# entries is fixed and checked by gpg-agent: A non-comment line starts\n"
|
||||
"# with optional white spaces, followed by exactly 40 hex character,\n"
|
||||
"# optioanlly followed by a flag character which my either be 'P', 'S'\n"
|
||||
"# or '*'. Additional data delimited with by a white space is ignored.\n"
|
||||
"\n";
|
||||
|
||||
|
||||
static FILE *trustfp;
|
||||
|
||||
|
||||
static int
|
||||
open_list (int append)
|
||||
{
|
||||
char *fname;
|
||||
|
||||
fname = make_filename (opt.homedir, "trustlist.txt", NULL);
|
||||
trustfp = fopen (fname, append? "a+":"r");
|
||||
if (!trustfp && errno == ENOENT)
|
||||
{
|
||||
trustfp = fopen (fname, "wx");
|
||||
if (!trustfp)
|
||||
{
|
||||
log_error ("can't create `%s': %s\n", fname, strerror (errno));
|
||||
xfree (fname);
|
||||
return seterr (File_Create_Error);
|
||||
}
|
||||
fputs (headerblurb, trustfp);
|
||||
fclose (trustfp);
|
||||
trustfp = fopen (fname, append? "a+":"r");
|
||||
}
|
||||
|
||||
if (!trustfp)
|
||||
{
|
||||
log_error ("can't open `%s': %s\n", fname, strerror (errno));
|
||||
xfree (fname);
|
||||
return seterr (File_Open_Error);
|
||||
}
|
||||
|
||||
/*FIXME: check the MAC */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Read the trustlist and return entry by entry. KEY must point to a
|
||||
buffer of at least 41 characters. KEYFLAG does return either 'P',
|
||||
'S' or '*'.
|
||||
|
||||
Reading a valid entry return 0, EOF returns -1 any other error
|
||||
returns the appropriate error code. */
|
||||
static int
|
||||
read_list (char *key, int *keyflag)
|
||||
{
|
||||
int rc;
|
||||
int c, i;
|
||||
char *p, line[256];
|
||||
|
||||
if (!trustfp)
|
||||
{
|
||||
rc = open_list (0);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (!fgets (line, DIM(line)-1, trustfp) )
|
||||
{
|
||||
if (feof (trustfp))
|
||||
return -1;
|
||||
return GNUPG_Read_Error;
|
||||
}
|
||||
|
||||
if (!*line || line[strlen(line)-1] != '\n')
|
||||
{
|
||||
/* eat until end of line */
|
||||
while ( (c=getc (trustfp)) != EOF && c != '\n')
|
||||
;
|
||||
return *line? GNUPG_Line_Too_Long: GNUPG_Incomplete_Line;
|
||||
}
|
||||
|
||||
/* Allow for emty lines and spaces */
|
||||
for (p=line; spacep (p); p++)
|
||||
;
|
||||
}
|
||||
while (!*p || *p == '\n' || *p == '#');
|
||||
|
||||
for (i=0; hexdigitp (p+i) && i < 40; i++)
|
||||
key[i] = p[i] >= 'a'? (p[i] & 0xdf): p[i];
|
||||
key[i] = 0;
|
||||
if (i!=40 || !(spacep (p+i) || p[i] == '\n'))
|
||||
{
|
||||
log_error ("invalid formatted fingerprint in trustlist\n");
|
||||
return GNUPG_Bad_Data;
|
||||
}
|
||||
assert (p[i]);
|
||||
if (p[i] == '\n')
|
||||
*keyflag = '*';
|
||||
else
|
||||
{
|
||||
i++;
|
||||
if ( p[i] == 'P' || p[i] == 'p')
|
||||
*keyflag = 'P';
|
||||
else if ( p[i] == 'S' || p[i] == 's')
|
||||
*keyflag = 'S';
|
||||
else if ( p[i] == '*')
|
||||
*keyflag = '*';
|
||||
else
|
||||
{
|
||||
log_error ("invalid keyflag in trustlist\n");
|
||||
return GNUPG_Bad_Data;
|
||||
}
|
||||
i++;
|
||||
if ( !(spacep (p+i) || p[i] == '\n'))
|
||||
{
|
||||
log_error ("invalid keyflag in trustlist\n");
|
||||
return GNUPG_Bad_Data;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check whether the given fpr is in our trustdb. We expect FPR to be
|
||||
an all uppercase hexstring of 40 characters. */
|
||||
int
|
||||
agent_istrusted (const char *fpr)
|
||||
{
|
||||
int rc;
|
||||
static char key[41];
|
||||
int keyflag;
|
||||
|
||||
if (trustfp)
|
||||
rewind (trustfp);
|
||||
while (!(rc=read_list (key, &keyflag)))
|
||||
{
|
||||
if (!strcmp (key, fpr))
|
||||
return 0;
|
||||
}
|
||||
if (rc != -1)
|
||||
{
|
||||
/* error in the trustdb - close it to give the user a chance for
|
||||
correction */
|
||||
fclose (trustfp);
|
||||
trustfp = NULL;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* write all trust entries to FP */
|
||||
int
|
||||
agent_listtrusted (void *assuan_context)
|
||||
{
|
||||
int rc;
|
||||
static char key[51];
|
||||
int keyflag;
|
||||
|
||||
if (trustfp)
|
||||
rewind (trustfp);
|
||||
while (!(rc=read_list (key, &keyflag)))
|
||||
{
|
||||
key[40] = ' ';
|
||||
key[41] = keyflag;
|
||||
key[42] = '\n';
|
||||
assuan_send_data (assuan_context, key, 43);
|
||||
assuan_send_data (assuan_context, NULL, 0); /* flush */
|
||||
}
|
||||
if (rc == -1)
|
||||
rc = 0;
|
||||
if (rc)
|
||||
{
|
||||
/* error in the trustdb - close it to give the user a chance for
|
||||
correction */
|
||||
fclose (trustfp);
|
||||
trustfp = NULL;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Insert the given fpr into our trustdb. We expect FPR to be an all
|
||||
uppercase hexstring of 40 characters. FLAG is either 'P' or 'C'.
|
||||
This function does first check whether that key has alreay ben put
|
||||
into the trustdb and returns success in this case. Before a FPR
|
||||
actually gets inserted, the user is asked by means of the pin-entry
|
||||
whether this is actual wants he want to do.
|
||||
*/
|
||||
int
|
||||
agent_marktrusted (const char *name, const char *fpr, int flag)
|
||||
{
|
||||
int rc;
|
||||
static char key[41];
|
||||
int keyflag;
|
||||
char *desc;
|
||||
|
||||
if (trustfp)
|
||||
rewind (trustfp);
|
||||
while (!(rc=read_list (key, &keyflag)))
|
||||
{
|
||||
if (!strcmp (key, fpr))
|
||||
return 0;
|
||||
}
|
||||
fclose (trustfp);
|
||||
trustfp = NULL;
|
||||
if (rc != -1)
|
||||
return rc; /* error in the trustdb */
|
||||
|
||||
/* insert a new one */
|
||||
if (asprintf (&desc,
|
||||
"Please verify that the certificate identified as:%%0A"
|
||||
" \"%s\"%%0A"
|
||||
"has the fingerprint:%%0A"
|
||||
" %s", name, fpr) < 0 )
|
||||
return GNUPG_Out_Of_Core;
|
||||
rc = agent_get_confirmation (desc, "Correct", "No");
|
||||
free (desc);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (asprintf (&desc,
|
||||
"Do you ultimately trust%%0A"
|
||||
" \"%s\"%%0A"
|
||||
"to correctly certify user certificates?",
|
||||
name) < 0 )
|
||||
return GNUPG_Out_Of_Core;
|
||||
rc = agent_get_confirmation (desc, "Yes", "No");
|
||||
free (desc);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* now check again to avoid duplicates. Also open in append mode now */
|
||||
rc = open_list (1);
|
||||
if (rc)
|
||||
return rc;
|
||||
rewind (trustfp);
|
||||
while (!(rc=read_list (key, &keyflag)))
|
||||
{
|
||||
if (!strcmp (key, fpr))
|
||||
return 0;
|
||||
}
|
||||
if (rc != -1)
|
||||
{
|
||||
fclose (trustfp);
|
||||
trustfp = NULL;
|
||||
return rc; /* error in the trustdb */
|
||||
}
|
||||
rc = 0;
|
||||
|
||||
/* append the key */
|
||||
fflush (trustfp);
|
||||
fputs ("\n# ", trustfp);
|
||||
print_sanitized_string (trustfp, name, 0);
|
||||
fprintf (trustfp, "\n%s %c\n", fpr, flag);
|
||||
if (ferror (trustfp))
|
||||
rc = GNUPG_Write_Error;
|
||||
|
||||
/* close because we are in append mode */
|
||||
if (fclose (trustfp))
|
||||
rc = GNUPG_File_Error;
|
||||
trustfp = NULL;
|
||||
return rc;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue