1
0
Fork 0
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:
Repo Admin 2002-10-19 07:55:27 +00:00
parent 8d76177f10
commit 82a17c9fb3
563 changed files with 0 additions and 267875 deletions

View file

@ -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.

View file

@ -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)

View file

@ -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*/

View file

@ -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;
}
}
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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*/

View file

@ -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;
}

File diff suppressed because it is too large Load diff

View file

@ -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*/

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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));
}

View file

@ -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*/

View file

@ -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;
}

View file

@ -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;
}