diff --git a/g10/apdu.c b/g10/apdu.c index 66cf30b1c..ffc7d36f1 100644 --- a/g10/apdu.c +++ b/g10/apdu.c @@ -1,3543 +1,3540 @@ /* apdu.c - ISO 7816 APDU functions and low level I/O * Copyright (C) 2003, 2004, 2008, 2009 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 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * $Id$ */ /* NOTE: This module is also used by other software, thus the use of the macro USE_GNU_PTH is mandatory. For GnuPG this macro is guaranteed to be defined true. */ #include #include #include #include #include #include #include #ifdef USE_GNU_PTH # include # include # include #endif /* If requested include the definitions for the remote APDU protocol code. */ #ifdef USE_G10CODE_RAPDU #include "rapdu.h" #endif /*USE_G10CODE_RAPDU*/ #if defined(GNUPG_SCD_MAIN_HEADER) #include GNUPG_SCD_MAIN_HEADER #elif GNUPG_MAJOR_VERSION == 1 /* This is used with GnuPG version < 1.9. The code has been source copied from the current GnuPG >= 1.9 and is maintained over there. */ #include "options.h" #include "errors.h" #include "memory.h" #include "util.h" #include "i18n.h" #include "dynload.h" #include "cardglue.h" #else /* GNUPG_MAJOR_VERSION != 1 */ #include "scdaemon.h" #include "exechelp.h" #endif /* GNUPG_MAJOR_VERSION != 1 */ +#include "../include/host2net.h" #include "apdu.h" #include "ccid-driver.h" /* Due to conflicting use of threading libraries we usually can't link against libpcsclite. Instead we use a wrapper program. */ #ifdef USE_GNU_PTH #if !defined(HAVE_W32_SYSTEM) && !defined(__CYGWIN__) #define NEED_PCSC_WRAPPER 1 #endif #endif #define MAX_READER 4 /* Number of readers we support concurrently. */ #if defined(_WIN32) || defined(__CYGWIN__) #define DLSTDCALL __stdcall #else #define DLSTDCALL #endif #if defined(__APPLE__) || defined(_WIN32) || defined(__CYGWIN__) typedef unsigned int pcsc_dword_t; #else typedef unsigned long pcsc_dword_t; #endif /* Helper to pass parameters related to keypad based operations. */ struct pininfo_s { int mode; int minlen; int maxlen; int padlen; }; /* A structure to collect information pertaining to one reader slot. */ struct reader_table_s { int used; /* True if slot is used. */ unsigned short port; /* Port number: 0 = unused, 1 - dev/tty */ /* Function pointers intialized to the various backends. */ int (*connect_card)(int); int (*disconnect_card)(int); int (*close_reader)(int); int (*shutdown_reader)(int); int (*reset_reader)(int); int (*get_status_reader)(int, unsigned int *); int (*send_apdu_reader)(int,unsigned char *,size_t, unsigned char *, size_t *, struct pininfo_s *); int (*check_keypad)(int, int, int, int, int, int); void (*dump_status_reader)(int); int (*set_progress_cb)(int, gcry_handler_progress_t, void*); struct { ccid_driver_t handle; } ccid; struct { long context; long card; pcsc_dword_t protocol; #ifdef NEED_PCSC_WRAPPER int req_fd; int rsp_fd; pid_t pid; #endif /*NEED_PCSC_WRAPPER*/ } pcsc; #ifdef USE_G10CODE_RAPDU struct { rapdu_t handle; } rapdu; #endif /*USE_G10CODE_RAPDU*/ char *rdrname; /* Name of the connected reader or NULL if unknown. */ int any_status; /* True if we have seen any status. */ int last_status; int status; int is_t0; /* True if we know that we are running T=0. */ unsigned char atr[33]; size_t atrlen; /* A zero length indicates that the ATR has not yet been read; i.e. the card is not ready for use. */ unsigned int change_counter; #ifdef USE_GNU_PTH int lock_initialized; pth_mutex_t lock; #endif }; typedef struct reader_table_s *reader_table_t; /* A global table to keep track of active readers. */ static struct reader_table_s reader_table[MAX_READER]; /* ct API function pointer. */ static char (* DLSTDCALL CT_init) (unsigned short ctn, unsigned short Pn); static char (* DLSTDCALL CT_data) (unsigned short ctn, unsigned char *dad, unsigned char *sad, unsigned short lc, unsigned char *cmd, unsigned short *lr, unsigned char *rsp); static char (* DLSTDCALL CT_close) (unsigned short ctn); /* PC/SC constants and function pointer. */ #define PCSC_SCOPE_USER 0 #define PCSC_SCOPE_TERMINAL 1 #define PCSC_SCOPE_SYSTEM 2 #define PCSC_SCOPE_GLOBAL 3 #define PCSC_PROTOCOL_T0 1 #define PCSC_PROTOCOL_T1 2 #define PCSC_PROTOCOL_RAW 4 #define PCSC_SHARE_EXCLUSIVE 1 #define PCSC_SHARE_SHARED 2 #define PCSC_SHARE_DIRECT 3 #define PCSC_LEAVE_CARD 0 #define PCSC_RESET_CARD 1 #define PCSC_UNPOWER_CARD 2 #define PCSC_EJECT_CARD 3 #define PCSC_UNKNOWN 0x0001 #define PCSC_ABSENT 0x0002 /* Card is absent. */ #define PCSC_PRESENT 0x0004 /* Card is present. */ #define PCSC_SWALLOWED 0x0008 /* Card is present and electrical connected. */ #define PCSC_POWERED 0x0010 /* Card is powered. */ #define PCSC_NEGOTIABLE 0x0020 /* Card is awaiting PTS. */ #define PCSC_SPECIFIC 0x0040 /* Card is ready for use. */ #define PCSC_STATE_UNAWARE 0x0000 /* Want status. */ #define PCSC_STATE_IGNORE 0x0001 /* Ignore this reader. */ #define PCSC_STATE_CHANGED 0x0002 /* State has changed. */ #define PCSC_STATE_UNKNOWN 0x0004 /* Reader unknown. */ #define PCSC_STATE_UNAVAILABLE 0x0008 /* Status unavailable. */ #define PCSC_STATE_EMPTY 0x0010 /* Card removed. */ #define PCSC_STATE_PRESENT 0x0020 /* Card inserted. */ #define PCSC_STATE_ATRMATCH 0x0040 /* ATR matches card. */ #define PCSC_STATE_EXCLUSIVE 0x0080 /* Exclusive Mode. */ #define PCSC_STATE_INUSE 0x0100 /* Shared mode. */ #define PCSC_STATE_MUTE 0x0200 /* Unresponsive card. */ /* Some PC/SC error codes. */ #define PCSC_E_CANCELLED 0x80100002 #define PCSC_E_CANT_DISPOSE 0x8010000E #define PCSC_E_INSUFFICIENT_BUFFER 0x80100008 #define PCSC_E_INVALID_ATR 0x80100015 #define PCSC_E_INVALID_HANDLE 0x80100003 #define PCSC_E_INVALID_PARAMETER 0x80100004 #define PCSC_E_INVALID_TARGET 0x80100005 #define PCSC_E_INVALID_VALUE 0x80100011 #define PCSC_E_NO_MEMORY 0x80100006 #define PCSC_E_UNKNOWN_READER 0x80100009 #define PCSC_E_TIMEOUT 0x8010000A #define PCSC_E_SHARING_VIOLATION 0x8010000B #define PCSC_E_NO_SMARTCARD 0x8010000C #define PCSC_E_UNKNOWN_CARD 0x8010000D #define PCSC_E_PROTO_MISMATCH 0x8010000F #define PCSC_E_NOT_READY 0x80100010 #define PCSC_E_SYSTEM_CANCELLED 0x80100012 #define PCSC_E_NOT_TRANSACTED 0x80100016 #define PCSC_E_READER_UNAVAILABLE 0x80100017 #define PCSC_W_REMOVED_CARD 0x80100069 /* The PC/SC error is defined as a long as per specs. Due to left shifts bit 31 will get sign extended. We use this mask to fix it. */ #define PCSC_ERR_MASK(a) ((a) & 0xffffffff) struct pcsc_io_request_s { unsigned long protocol; unsigned long pci_len; }; typedef struct pcsc_io_request_s *pcsc_io_request_t; #ifdef __APPLE__ # pragma pack(1) #endif struct pcsc_readerstate_s { const char *reader; void *user_data; pcsc_dword_t current_state; pcsc_dword_t event_state; pcsc_dword_t atrlen; unsigned char atr[33]; }; #ifdef __APPLE__ # pragma pack() #endif typedef struct pcsc_readerstate_s *pcsc_readerstate_t; long (* DLSTDCALL pcsc_establish_context) (pcsc_dword_t scope, const void *reserved1, const void *reserved2, long *r_context); long (* DLSTDCALL pcsc_release_context) (long context); long (* DLSTDCALL pcsc_list_readers) (long context, const char *groups, char *readers, pcsc_dword_t *readerslen); long (* DLSTDCALL pcsc_get_status_change) (long context, pcsc_dword_t timeout, pcsc_readerstate_t readerstates, pcsc_dword_t nreaderstates); long (* DLSTDCALL pcsc_connect) (long context, const char *reader, pcsc_dword_t share_mode, pcsc_dword_t preferred_protocols, long *r_card, pcsc_dword_t *r_active_protocol); long (* DLSTDCALL pcsc_reconnect) (long card, pcsc_dword_t share_mode, pcsc_dword_t preferred_protocols, pcsc_dword_t initialization, pcsc_dword_t *r_active_protocol); long (* DLSTDCALL pcsc_disconnect) (long card, pcsc_dword_t disposition); long (* DLSTDCALL pcsc_status) (long card, char *reader, pcsc_dword_t *readerlen, pcsc_dword_t *r_state, pcsc_dword_t *r_protocol, unsigned char *atr, pcsc_dword_t *atrlen); long (* DLSTDCALL pcsc_begin_transaction) (long card); long (* DLSTDCALL pcsc_end_transaction) (long card, pcsc_dword_t disposition); long (* DLSTDCALL pcsc_transmit) (long card, const pcsc_io_request_t send_pci, const unsigned char *send_buffer, pcsc_dword_t send_len, pcsc_io_request_t recv_pci, unsigned char *recv_buffer, pcsc_dword_t *recv_len); long (* DLSTDCALL pcsc_set_timeout) (long context, pcsc_dword_t timeout); /* Prototypes. */ static int pcsc_get_status (int slot, unsigned int *status); static int reset_pcsc_reader (int slot); static int apdu_get_status_internal (int slot, int hang, int no_atr_reset, unsigned int *status, unsigned int *changed); /* Helper */ /* Find an unused reader slot for PORTSTR and put it into the reader table. Return -1 on error or the index into the reader table. */ static int new_reader_slot (void) { int i, reader = -1; for (i=0; i < MAX_READER; i++) { if (!reader_table[i].used && reader == -1) reader = i; } if (reader == -1) { log_error ("new_reader_slot: out of slots\n"); return -1; } #ifdef USE_GNU_PTH if (!reader_table[reader].lock_initialized) { if (!pth_mutex_init (&reader_table[reader].lock)) { log_error ("error initializing mutex: %s\n", strerror (errno)); return -1; } reader_table[reader].lock_initialized = 1; } #endif /*USE_GNU_PTH*/ reader_table[reader].connect_card = NULL; reader_table[reader].disconnect_card = NULL; reader_table[reader].close_reader = NULL; reader_table[reader].shutdown_reader = NULL; reader_table[reader].reset_reader = NULL; reader_table[reader].get_status_reader = NULL; reader_table[reader].send_apdu_reader = NULL; reader_table[reader].check_keypad = NULL; reader_table[reader].dump_status_reader = NULL; reader_table[reader].set_progress_cb = NULL; reader_table[reader].used = 1; reader_table[reader].any_status = 0; reader_table[reader].last_status = 0; reader_table[reader].is_t0 = 1; #ifdef NEED_PCSC_WRAPPER reader_table[reader].pcsc.req_fd = -1; reader_table[reader].pcsc.rsp_fd = -1; reader_table[reader].pcsc.pid = (pid_t)(-1); #endif return reader; } static void dump_reader_status (int slot) { if (!opt.verbose) return; if (reader_table[slot].dump_status_reader) reader_table[slot].dump_status_reader (slot); if (reader_table[slot].status != -1 && reader_table[slot].atrlen) { log_info ("slot %d: ATR=", slot); log_printhex ("", reader_table[slot].atr, reader_table[slot].atrlen); } } static const char * host_sw_string (long err) { switch (err) { case 0: return "okay"; case SW_HOST_OUT_OF_CORE: return "out of core"; case SW_HOST_INV_VALUE: return "invalid value"; case SW_HOST_NO_DRIVER: return "no driver"; case SW_HOST_NOT_SUPPORTED: return "not supported"; case SW_HOST_LOCKING_FAILED: return "locking failed"; case SW_HOST_BUSY: return "busy"; case SW_HOST_NO_CARD: return "no card"; case SW_HOST_CARD_INACTIVE: return "card inactive"; case SW_HOST_CARD_IO_ERROR: return "card I/O error"; case SW_HOST_GENERAL_ERROR: return "general error"; case SW_HOST_NO_READER: return "no reader"; case SW_HOST_ABORTED: return "aborted"; case SW_HOST_NO_KEYPAD: return "no keypad"; case SW_HOST_ALREADY_CONNECTED: return "already connected"; default: return "unknown host status error"; } } const char * apdu_strerror (int rc) { switch (rc) { case SW_EOF_REACHED : return "eof reached"; case SW_EEPROM_FAILURE : return "eeprom failure"; case SW_WRONG_LENGTH : return "wrong length"; case SW_CHV_WRONG : return "CHV wrong"; case SW_CHV_BLOCKED : return "CHV blocked"; case SW_USE_CONDITIONS : return "use conditions not satisfied"; case SW_BAD_PARAMETER : return "bad parameter"; case SW_NOT_SUPPORTED : return "not supported"; case SW_FILE_NOT_FOUND : return "file not found"; case SW_RECORD_NOT_FOUND:return "record not found"; case SW_REF_NOT_FOUND : return "reference not found"; case SW_BAD_LC : return "bad Lc"; case SW_BAD_P0_P1 : return "bad P0 or P1"; case SW_INS_NOT_SUP : return "instruction not supported"; case SW_CLA_NOT_SUP : return "class not supported"; case SW_SUCCESS : return "success"; default: if ((rc & ~0x00ff) == SW_MORE_DATA) return "more data available"; if ( (rc & 0x10000) ) return host_sw_string (rc); return "unknown status error"; } } /* ct API Interface */ static const char * ct_error_string (long err) { switch (err) { case 0: return "okay"; case -1: return "invalid data"; case -8: return "ct error"; case -10: return "transmission error"; case -11: return "memory allocation error"; case -128: return "HTSI error"; default: return "unknown CT-API error"; } } static void ct_dump_reader_status (int slot) { log_info ("reader slot %d: %s\n", slot, reader_table[slot].status == 1? "Processor ICC present" : reader_table[slot].status == 0? "Memory ICC present" : "ICC not present" ); } /* Wait for the card in SLOT and activate it. Return a status word error or 0 on success. */ static int ct_activate_card (int slot) { int rc; unsigned char dad[1], sad[1], cmd[11], buf[256]; unsigned short buflen; /* Check whether card has been inserted. */ dad[0] = 1; /* Destination address: CT. */ sad[0] = 2; /* Source address: Host. */ cmd[0] = 0x20; /* Class byte. */ cmd[1] = 0x13; /* Request status. */ cmd[2] = 0x00; /* From kernel. */ cmd[3] = 0x80; /* Return card's DO. */ cmd[4] = 0x00; buflen = DIM(buf); rc = CT_data (slot, dad, sad, 5, cmd, &buflen, buf); if (rc || buflen < 2 || buf[buflen-2] != 0x90) { log_error ("ct_activate_card: can't get status of reader %d: %s\n", slot, ct_error_string (rc)); return SW_HOST_CARD_IO_ERROR; } /* Connected, now activate the card. */ dad[0] = 1; /* Destination address: CT. */ sad[0] = 2; /* Source address: Host. */ cmd[0] = 0x20; /* Class byte. */ cmd[1] = 0x12; /* Request ICC. */ cmd[2] = 0x01; /* From first interface. */ cmd[3] = 0x01; /* Return card's ATR. */ cmd[4] = 0x00; buflen = DIM(buf); rc = CT_data (slot, dad, sad, 5, cmd, &buflen, buf); if (rc || buflen < 2 || buf[buflen-2] != 0x90) { log_error ("ct_activate_card(%d): activation failed: %s\n", slot, ct_error_string (rc)); if (!rc) log_printhex (" received data:", buf, buflen); return SW_HOST_CARD_IO_ERROR; } /* Store the type and the ATR. */ if (buflen - 2 > DIM (reader_table[0].atr)) { log_error ("ct_activate_card(%d): ATR too long\n", slot); return SW_HOST_CARD_IO_ERROR; } reader_table[slot].status = buf[buflen - 1]; memcpy (reader_table[slot].atr, buf, buflen - 2); reader_table[slot].atrlen = buflen - 2; return 0; } static int close_ct_reader (int slot) { CT_close (slot); reader_table[slot].used = 0; return 0; } static int reset_ct_reader (int slot) { /* FIXME: Check is this is sufficient do do a reset. */ return ct_activate_card (slot); } static int ct_get_status (int slot, unsigned int *status) { (void)slot; /* The status we returned is wrong but we don't care becuase ctAPI is not anymore required. */ *status = APDU_CARD_USABLE|APDU_CARD_PRESENT|APDU_CARD_ACTIVE; return 0; } /* Actually send the APDU of length APDULEN to SLOT and return a maximum of *BUFLEN data in BUFFER, the actual retruned size will be set to BUFLEN. Returns: CT API error code. */ static int ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen, unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo) { int rc; unsigned char dad[1], sad[1]; unsigned short ctbuflen; (void)pininfo; /* If we don't have an ATR, we need to reset the reader first. */ if (!reader_table[slot].atrlen && (rc = reset_ct_reader (slot))) return rc; dad[0] = 0; /* Destination address: Card. */ sad[0] = 2; /* Source address: Host. */ ctbuflen = *buflen; if (DBG_CARD_IO) log_printhex (" CT_data:", apdu, apdulen); rc = CT_data (slot, dad, sad, apdulen, apdu, &ctbuflen, buffer); *buflen = ctbuflen; return rc? SW_HOST_CARD_IO_ERROR: 0; } /* Open a reader and return an internal handle for it. PORT is a non-negative value with the port number of the reader. USB readers do have port numbers starting at 32769. */ static int open_ct_reader (int port) { int rc, reader; if (port < 0 || port > 0xffff) { log_error ("open_ct_reader: invalid port %d requested\n", port); return -1; } reader = new_reader_slot (); if (reader == -1) return reader; reader_table[reader].port = port; rc = CT_init (reader, (unsigned short)port); if (rc) { log_error ("apdu_open_ct_reader failed on port %d: %s\n", port, ct_error_string (rc)); reader_table[reader].used = 0; return -1; } /* Only try to activate the card. */ rc = ct_activate_card (reader); if (rc) { reader_table[reader].atrlen = 0; rc = 0; } reader_table[reader].close_reader = close_ct_reader; reader_table[reader].reset_reader = reset_ct_reader; reader_table[reader].get_status_reader = ct_get_status; reader_table[reader].send_apdu_reader = ct_send_apdu; reader_table[reader].check_keypad = NULL; reader_table[reader].dump_status_reader = ct_dump_reader_status; dump_reader_status (reader); return reader; } /* PC/SC Interface */ #ifdef NEED_PCSC_WRAPPER static int writen (int fd, const void *buf, size_t nbytes) { size_t nleft = nbytes; int nwritten; /* log_printhex (" writen:", buf, nbytes); */ while (nleft > 0) { #ifdef USE_GNU_PTH nwritten = pth_write (fd, buf, nleft); #else nwritten = write (fd, buf, nleft); #endif if (nwritten < 0 && errno == EINTR) continue; if (nwritten < 0) return -1; nleft -= nwritten; buf = (const char*)buf + nwritten; } return 0; } /* Read up to BUFLEN bytes from FD and return the number of bytes actually read in NREAD. Returns -1 on error or 0 on success. */ static int readn (int fd, void *buf, size_t buflen, size_t *nread) { size_t nleft = buflen; int n; /* void *orig_buf = buf; */ while (nleft > 0) { #ifdef USE_GNU_PTH # ifdef HAVE_W32_SYSTEM # error Cannot use pth_read here because it expects a system HANDLE. # endif n = pth_read (fd, buf, nleft); #else n = read (fd, buf, nleft); #endif if (n < 0 && errno == EINTR) continue; if (n < 0) return -1; /* read error. */ if (!n) break; /* EOF */ nleft -= n; buf = (char*)buf + n; } if (nread) *nread = buflen - nleft; /* log_printhex (" readn:", orig_buf, *nread); */ return 0; } #endif /*NEED_PCSC_WRAPPER*/ static const char * pcsc_error_string (long err) { const char *s; if (!err) return "okay"; if ((err & 0x80100000) != 0x80100000) return "invalid PC/SC error code"; err &= 0xffff; switch (err) { case 0x0002: s = "cancelled"; break; case 0x000e: s = "can't dispose"; break; case 0x0008: s = "insufficient buffer"; break; case 0x0015: s = "invalid ATR"; break; case 0x0003: s = "invalid handle"; break; case 0x0004: s = "invalid parameter"; break; case 0x0005: s = "invalid target"; break; case 0x0011: s = "invalid value"; break; case 0x0006: s = "no memory"; break; case 0x0013: s = "comm error"; break; case 0x0001: s = "internal error"; break; case 0x0014: s = "unknown error"; break; case 0x0007: s = "waited too long"; break; case 0x0009: s = "unknown reader"; break; case 0x000a: s = "timeout"; break; case 0x000b: s = "sharing violation"; break; case 0x000c: s = "no smartcard"; break; case 0x000d: s = "unknown card"; break; case 0x000f: s = "proto mismatch"; break; case 0x0010: s = "not ready"; break; case 0x0012: s = "system cancelled"; break; case 0x0016: s = "not transacted"; break; case 0x0017: s = "reader unavailable"; break; case 0x0065: s = "unsupported card"; break; case 0x0066: s = "unresponsive card"; break; case 0x0067: s = "unpowered card"; break; case 0x0068: s = "reset card"; break; case 0x0069: s = "removed card"; break; case 0x006a: s = "inserted card"; break; case 0x001f: s = "unsupported feature"; break; case 0x0019: s = "PCI too small"; break; case 0x001a: s = "reader unsupported"; break; case 0x001b: s = "duplicate reader"; break; case 0x001c: s = "card unsupported"; break; case 0x001d: s = "no service"; break; case 0x001e: s = "service stopped"; break; default: s = "unknown PC/SC error code"; break; } return s; } /* Map PC/SC error codes to our special host status words. */ static int pcsc_error_to_sw (long ec) { int rc; switch ( PCSC_ERR_MASK (ec) ) { case 0: rc = 0; break; case PCSC_E_CANCELLED: rc = SW_HOST_ABORTED; break; case PCSC_E_NO_MEMORY: rc = SW_HOST_OUT_OF_CORE; break; case PCSC_E_TIMEOUT: rc = SW_HOST_CARD_IO_ERROR; break; case PCSC_E_SHARING_VIOLATION: rc = SW_HOST_LOCKING_FAILED; break; case PCSC_E_NO_SMARTCARD: rc = SW_HOST_NO_CARD; break; case PCSC_W_REMOVED_CARD: rc = SW_HOST_NO_CARD; break; case PCSC_E_INVALID_TARGET: case PCSC_E_INVALID_VALUE: case PCSC_E_INVALID_HANDLE: case PCSC_E_INVALID_PARAMETER: case PCSC_E_INSUFFICIENT_BUFFER: rc = SW_HOST_INV_VALUE; break; default: rc = SW_HOST_GENERAL_ERROR; break; } return rc; } static void dump_pcsc_reader_status (int slot) { if (reader_table[slot].pcsc.card) { log_info ("reader slot %d: active protocol:", slot); if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T0)) log_printf (" T0"); else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1)) log_printf (" T1"); else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_RAW)) log_printf (" raw"); log_printf ("\n"); } else log_info ("reader slot %d: not connected\n", slot); } #ifndef NEED_PCSC_WRAPPER static int pcsc_get_status_direct (int slot, unsigned int *status) { long err; struct pcsc_readerstate_s rdrstates[1]; memset (rdrstates, 0, sizeof *rdrstates); rdrstates[0].reader = reader_table[slot].rdrname; rdrstates[0].current_state = PCSC_STATE_UNAWARE; err = pcsc_get_status_change (reader_table[slot].pcsc.context, 0, rdrstates, 1); if (err == PCSC_E_TIMEOUT) err = 0; /* Timeout is no error error here. */ if (err) { log_error ("pcsc_get_status_change failed: %s (0x%lx)\n", pcsc_error_string (err), err); return pcsc_error_to_sw (err); } /* log_debug */ /* ("pcsc_get_status_change: %s%s%s%s%s%s%s%s%s%s\n", */ /* (rdrstates[0].event_state & PCSC_STATE_IGNORE)? " ignore":"", */ /* (rdrstates[0].event_state & PCSC_STATE_CHANGED)? " changed":"", */ /* (rdrstates[0].event_state & PCSC_STATE_UNKNOWN)? " unknown":"", */ /* (rdrstates[0].event_state & PCSC_STATE_UNAVAILABLE)?" unavail":"", */ /* (rdrstates[0].event_state & PCSC_STATE_EMPTY)? " empty":"", */ /* (rdrstates[0].event_state & PCSC_STATE_PRESENT)? " present":"", */ /* (rdrstates[0].event_state & PCSC_STATE_ATRMATCH)? " atr":"", */ /* (rdrstates[0].event_state & PCSC_STATE_EXCLUSIVE)? " excl":"", */ /* (rdrstates[0].event_state & PCSC_STATE_INUSE)? " unuse":"", */ /* (rdrstates[0].event_state & PCSC_STATE_MUTE)? " mute":"" ); */ *status = 0; if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) ) *status |= APDU_CARD_PRESENT; if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) ) *status |= APDU_CARD_ACTIVE; #ifndef HAVE_W32_SYSTEM /* We indicate a useful card if it is not in use by another application. This is because we only use exclusive access mode. */ if ( (*status & (APDU_CARD_PRESENT|APDU_CARD_ACTIVE)) == (APDU_CARD_PRESENT|APDU_CARD_ACTIVE) && !(rdrstates[0].event_state & PCSC_STATE_INUSE) ) *status |= APDU_CARD_USABLE; #else /* Some winscard drivers may set EXCLUSIVE and INUSE at the same time when we are the only user (SCM SCR335) under Windows. */ if ((*status & (APDU_CARD_PRESENT|APDU_CARD_ACTIVE)) == (APDU_CARD_PRESENT|APDU_CARD_ACTIVE)) *status |= APDU_CARD_USABLE; #endif return 0; } #endif /*!NEED_PCSC_WRAPPER*/ #ifdef NEED_PCSC_WRAPPER static int pcsc_get_status_wrapped (int slot, unsigned int *status) { long err; reader_table_t slotp; size_t len, full_len; int i, n; unsigned char msgbuf[9]; unsigned char buffer[16]; int sw = SW_HOST_CARD_IO_ERROR; slotp = reader_table + slot; if (slotp->pcsc.req_fd == -1 || slotp->pcsc.rsp_fd == -1 || slotp->pcsc.pid == (pid_t)(-1) ) { log_error ("pcsc_get_status: pcsc-wrapper not running\n"); return sw; } msgbuf[0] = 0x04; /* STATUS command. */ len = 0; msgbuf[1] = (len >> 24); msgbuf[2] = (len >> 16); msgbuf[3] = (len >> 8); msgbuf[4] = (len ); if ( writen (slotp->pcsc.req_fd, msgbuf, 5) ) { log_error ("error sending PC/SC STATUS request: %s\n", strerror (errno)); goto command_failed; } /* Read the response. */ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9) { log_error ("error receiving PC/SC STATUS response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } - len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4]; + len = buf32_to_size_t (msgbuf+1); if (msgbuf[0] != 0x81 || len < 4) { log_error ("invalid response header from PC/SC received\n"); goto command_failed; } len -= 4; /* Already read the error code. */ - err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16) - | (msgbuf[7] << 8 ) | msgbuf[8]); + err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5)); if (err) { log_error ("pcsc_status failed: %s (0x%lx)\n", pcsc_error_string (err), err); /* This is a proper error code, so return immediately. */ return pcsc_error_to_sw (err); } full_len = len; /* The current version returns 3 words but we allow also for old versions returning only 2 words. */ n = 12 < len ? 12 : len; if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || (len != 8 && len != 12)) { log_error ("error receiving PC/SC STATUS response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } slotp->is_t0 = (len == 12 && !!(buffer[11] & PCSC_PROTOCOL_T0)); full_len -= len; /* Newer versions of the wrapper might send more status bytes. Read them. */ while (full_len) { unsigned char dummybuf[128]; n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf); if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n) { log_error ("error receiving PC/SC TRANSMIT response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } full_len -= n; } /* We are lucky: The wrapper already returns the data in the required format. */ *status = buffer[3]; return 0; command_failed: close (slotp->pcsc.req_fd); close (slotp->pcsc.rsp_fd); slotp->pcsc.req_fd = -1; slotp->pcsc.rsp_fd = -1; kill (slotp->pcsc.pid, SIGTERM); slotp->pcsc.pid = (pid_t)(-1); slotp->used = 0; return sw; } #endif /*NEED_PCSC_WRAPPER*/ static int pcsc_get_status (int slot, unsigned int *status) { #ifdef NEED_PCSC_WRAPPER return pcsc_get_status_wrapped (slot, status); #else return pcsc_get_status_direct (slot, status); #endif } #ifndef NEED_PCSC_WRAPPER static int pcsc_send_apdu_direct (int slot, unsigned char *apdu, size_t apdulen, unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo) { long err; struct pcsc_io_request_s send_pci; pcsc_dword_t recv_len; if (!reader_table[slot].atrlen && (err = reset_pcsc_reader (slot))) return err; if (DBG_CARD_IO) log_printhex (" PCSC_data:", apdu, apdulen); if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1)) send_pci.protocol = PCSC_PROTOCOL_T1; else send_pci.protocol = PCSC_PROTOCOL_T0; send_pci.pci_len = sizeof send_pci; recv_len = *buflen; err = pcsc_transmit (reader_table[slot].pcsc.card, &send_pci, apdu, apdulen, NULL, buffer, &recv_len); *buflen = recv_len; if (err) log_error ("pcsc_transmit failed: %s (0x%lx)\n", pcsc_error_string (err), err); return pcsc_error_to_sw (err); } #endif /*!NEED_PCSC_WRAPPER*/ #ifdef NEED_PCSC_WRAPPER static int pcsc_send_apdu_wrapped (int slot, unsigned char *apdu, size_t apdulen, unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo) { long err; reader_table_t slotp; size_t len, full_len; int i, n; unsigned char msgbuf[9]; int sw = SW_HOST_CARD_IO_ERROR; (void)pininfo; if (!reader_table[slot].atrlen && (err = reset_pcsc_reader (slot))) return err; if (DBG_CARD_IO) log_printhex (" PCSC_data:", apdu, apdulen); slotp = reader_table + slot; if (slotp->pcsc.req_fd == -1 || slotp->pcsc.rsp_fd == -1 || slotp->pcsc.pid == (pid_t)(-1) ) { log_error ("pcsc_send_apdu: pcsc-wrapper not running\n"); return sw; } msgbuf[0] = 0x03; /* TRANSMIT command. */ len = apdulen; msgbuf[1] = (len >> 24); msgbuf[2] = (len >> 16); msgbuf[3] = (len >> 8); msgbuf[4] = (len ); if ( writen (slotp->pcsc.req_fd, msgbuf, 5) || writen (slotp->pcsc.req_fd, apdu, len)) { log_error ("error sending PC/SC TRANSMIT request: %s\n", strerror (errno)); goto command_failed; } /* Read the response. */ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9) { log_error ("error receiving PC/SC TRANSMIT response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } - len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4]; + len = buf32_to_size_t (msgbuf+1); if (msgbuf[0] != 0x81 || len < 4) { log_error ("invalid response header from PC/SC received\n"); goto command_failed; } len -= 4; /* Already read the error code. */ - err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16) - | (msgbuf[7] << 8 ) | msgbuf[8]); + err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5)); if (err) { log_error ("pcsc_transmit failed: %s (0x%lx)\n", pcsc_error_string (err), err); return pcsc_error_to_sw (err); } full_len = len; n = *buflen < len ? *buflen : len; if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n) { log_error ("error receiving PC/SC TRANSMIT response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } *buflen = n; full_len -= len; if (full_len) { log_error ("pcsc_send_apdu: provided buffer too short - truncated\n"); err = SW_HOST_INV_VALUE; } /* We need to read any rest of the response, to keep the protocol running. */ while (full_len) { unsigned char dummybuf[128]; n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf); if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n) { log_error ("error receiving PC/SC TRANSMIT response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } full_len -= n; } return err; command_failed: close (slotp->pcsc.req_fd); close (slotp->pcsc.rsp_fd); slotp->pcsc.req_fd = -1; slotp->pcsc.rsp_fd = -1; kill (slotp->pcsc.pid, SIGTERM); slotp->pcsc.pid = (pid_t)(-1); slotp->used = 0; return sw; } #endif /*NEED_PCSC_WRAPPER*/ /* Send the APDU of length APDULEN to SLOT and return a maximum of *BUFLEN data in BUFFER, the actual returned size will be stored at BUFLEN. Returns: A status word. */ static int pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo) { #ifdef NEED_PCSC_WRAPPER return pcsc_send_apdu_wrapped (slot, apdu, apdulen, buffer, buflen, pininfo); #else return pcsc_send_apdu_direct (slot, apdu, apdulen, buffer, buflen, pininfo); #endif } #ifndef NEED_PCSC_WRAPPER static int close_pcsc_reader_direct (int slot) { pcsc_release_context (reader_table[slot].pcsc.context); xfree (reader_table[slot].rdrname); reader_table[slot].rdrname = NULL; reader_table[slot].used = 0; return 0; } #endif /*!NEED_PCSC_WRAPPER*/ #ifdef NEED_PCSC_WRAPPER static int close_pcsc_reader_wrapped (int slot) { long err; reader_table_t slotp; size_t len; int i; unsigned char msgbuf[9]; slotp = reader_table + slot; if (slotp->pcsc.req_fd == -1 || slotp->pcsc.rsp_fd == -1 || slotp->pcsc.pid == (pid_t)(-1) ) { log_error ("close_pcsc_reader: pcsc-wrapper not running\n"); return 0; } msgbuf[0] = 0x02; /* CLOSE command. */ len = 0; msgbuf[1] = (len >> 24); msgbuf[2] = (len >> 16); msgbuf[3] = (len >> 8); msgbuf[4] = (len ); if ( writen (slotp->pcsc.req_fd, msgbuf, 5) ) { log_error ("error sending PC/SC CLOSE request: %s\n", strerror (errno)); goto command_failed; } /* Read the response. */ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9) { log_error ("error receiving PC/SC CLOSE response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } - len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4]; + len = buf32_to_size_t (msgbuf+1); if (msgbuf[0] != 0x81 || len < 4) { log_error ("invalid response header from PC/SC received\n"); goto command_failed; } len -= 4; /* Already read the error code. */ - err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16) - | (msgbuf[7] << 8 ) | msgbuf[8]); + err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5)); if (err) log_error ("pcsc_close failed: %s (0x%lx)\n", pcsc_error_string (err), err); /* We will close the wrapper in any case - errors are merely informational. */ command_failed: close (slotp->pcsc.req_fd); close (slotp->pcsc.rsp_fd); slotp->pcsc.req_fd = -1; slotp->pcsc.rsp_fd = -1; kill (slotp->pcsc.pid, SIGTERM); slotp->pcsc.pid = (pid_t)(-1); slotp->used = 0; return 0; } #endif /*NEED_PCSC_WRAPPER*/ static int close_pcsc_reader (int slot) { #ifdef NEED_PCSC_WRAPPER return close_pcsc_reader_wrapped (slot); #else return close_pcsc_reader_direct (slot); #endif } /* Connect a PC/SC card. */ #ifndef NEED_PCSC_WRAPPER static int connect_pcsc_card (int slot) { long err; assert (slot >= 0 && slot < MAX_READER); if (reader_table[slot].pcsc.card) return SW_HOST_ALREADY_CONNECTED; reader_table[slot].atrlen = 0; reader_table[slot].last_status = 0; reader_table[slot].is_t0 = 0; err = pcsc_connect (reader_table[slot].pcsc.context, reader_table[slot].rdrname, PCSC_SHARE_EXCLUSIVE, PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1, &reader_table[slot].pcsc.card, &reader_table[slot].pcsc.protocol); if (err) { reader_table[slot].pcsc.card = 0; if (err != PCSC_E_NO_SMARTCARD) log_error ("pcsc_connect failed: %s (0x%lx)\n", pcsc_error_string (err), err); } else { char reader[250]; pcsc_dword_t readerlen, atrlen; pcsc_dword_t card_state, card_protocol; atrlen = DIM (reader_table[0].atr); readerlen = sizeof reader -1 ; err = pcsc_status (reader_table[slot].pcsc.card, reader, &readerlen, &card_state, &card_protocol, reader_table[slot].atr, &atrlen); if (err) log_error ("pcsc_status failed: %s (0x%lx) %lu\n", pcsc_error_string (err), (unsigned long)err, (unsigned long)readerlen); else { if (atrlen > DIM (reader_table[0].atr)) log_bug ("ATR returned by pcsc_status is too large\n"); reader_table[slot].atrlen = atrlen; /* If we got to here we know that a card is present and usable. Remember this. */ reader_table[slot].last_status = ( APDU_CARD_USABLE | APDU_CARD_PRESENT | APDU_CARD_ACTIVE); reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0); } } dump_reader_status (slot); return pcsc_error_to_sw (err); } #endif /*!NEED_PCSC_WRAPPER*/ /* Disconnect a PC/SC card. Note that this succeeds even if the card is not connected. */ #ifndef NEED_PCSC_WRAPPER static int disconnect_pcsc_card (int slot) { long err; assert (slot >= 0 && slot < MAX_READER); if (!reader_table[slot].pcsc.card) return 0; err = pcsc_disconnect (reader_table[slot].pcsc.card, PCSC_LEAVE_CARD); if (err) { log_error ("pcsc_disconnect failed: %s (0x%lx)\n", pcsc_error_string (err), err); return SW_HOST_CARD_IO_ERROR; } reader_table[slot].pcsc.card = 0; return 0; } #endif /*!NEED_PCSC_WRAPPER*/ #ifndef NEED_PCSC_WRAPPER static int reset_pcsc_reader_direct (int slot) { int sw; sw = disconnect_pcsc_card (slot); if (!sw) sw = connect_pcsc_card (slot); return sw; } #endif /*NEED_PCSC_WRAPPER*/ #ifdef NEED_PCSC_WRAPPER static int reset_pcsc_reader_wrapped (int slot) { long err; reader_table_t slotp; size_t len; int i, n; unsigned char msgbuf[9]; unsigned int dummy_status; int sw = SW_HOST_CARD_IO_ERROR; slotp = reader_table + slot; if (slotp->pcsc.req_fd == -1 || slotp->pcsc.rsp_fd == -1 || slotp->pcsc.pid == (pid_t)(-1) ) { log_error ("pcsc_get_status: pcsc-wrapper not running\n"); return sw; } msgbuf[0] = 0x05; /* RESET command. */ len = 0; msgbuf[1] = (len >> 24); msgbuf[2] = (len >> 16); msgbuf[3] = (len >> 8); msgbuf[4] = (len ); if ( writen (slotp->pcsc.req_fd, msgbuf, 5) ) { log_error ("error sending PC/SC RESET request: %s\n", strerror (errno)); goto command_failed; } /* Read the response. */ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9) { log_error ("error receiving PC/SC RESET response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } - len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4]; + len = buf32_to_size_t (msgbuf+1); if (msgbuf[0] != 0x81 || len < 4) { log_error ("invalid response header from PC/SC received\n"); goto command_failed; } len -= 4; /* Already read the error code. */ if (len > DIM (slotp->atr)) { log_error ("PC/SC returned a too large ATR (len=%lx)\n", (unsigned long)len); sw = SW_HOST_GENERAL_ERROR; goto command_failed; } - err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16) - | (msgbuf[7] << 8 ) | msgbuf[8]); + err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5)); if (err) { log_error ("PC/SC RESET failed: %s (0x%lx)\n", pcsc_error_string (err), err); /* If the error code is no smart card, we should not considere this a major error and close the wrapper. */ sw = pcsc_error_to_sw (err); if (err == PCSC_E_NO_SMARTCARD) return sw; goto command_failed; } /* The open function may return a zero for the ATR length to indicate that no card is present. */ n = len; if (n) { if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n) { log_error ("error receiving PC/SC RESET response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } } slotp->atrlen = len; /* Read the status so that IS_T0 will be set. */ pcsc_get_status (slot, &dummy_status); return 0; command_failed: close (slotp->pcsc.req_fd); close (slotp->pcsc.rsp_fd); slotp->pcsc.req_fd = -1; slotp->pcsc.rsp_fd = -1; kill (slotp->pcsc.pid, SIGTERM); slotp->pcsc.pid = (pid_t)(-1); slotp->used = 0; return sw; } #endif /* !NEED_PCSC_WRAPPER */ /* Send an PC/SC reset command and return a status word on error or 0 on success. */ static int reset_pcsc_reader (int slot) { #ifdef NEED_PCSC_WRAPPER return reset_pcsc_reader_wrapped (slot); #else return reset_pcsc_reader_direct (slot); #endif } /* Open the PC/SC reader without using the wrapper. Returns -1 on error or a slot number for the reader. */ #ifndef NEED_PCSC_WRAPPER static int open_pcsc_reader_direct (const char *portstr) { long err; int slot; char *list = NULL; pcsc_dword_t nreader; char *p; slot = new_reader_slot (); if (slot == -1) return -1; /* Fixme: Allocating a context for each slot is not required. One global context should be sufficient. */ err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL, &reader_table[slot].pcsc.context); if (err) { log_error ("pcsc_establish_context failed: %s (0x%lx)\n", pcsc_error_string (err), err); reader_table[slot].used = 0; return -1; } err = pcsc_list_readers (reader_table[slot].pcsc.context, NULL, NULL, &nreader); if (!err) { list = xtrymalloc (nreader+1); /* Better add 1 for safety reasons. */ if (!list) { log_error ("error allocating memory for reader list\n"); pcsc_release_context (reader_table[slot].pcsc.context); reader_table[slot].used = 0; return -1 /*SW_HOST_OUT_OF_CORE*/; } err = pcsc_list_readers (reader_table[slot].pcsc.context, NULL, list, &nreader); } if (err) { log_error ("pcsc_list_readers failed: %s (0x%lx)\n", pcsc_error_string (err), err); pcsc_release_context (reader_table[slot].pcsc.context); reader_table[slot].used = 0; xfree (list); return -1; } p = list; while (nreader) { if (!*p && !p[1]) break; if (*p) log_info ("detected reader `%s'\n", p); if (nreader < (strlen (p)+1)) { log_error ("invalid response from pcsc_list_readers\n"); break; } nreader -= strlen (p)+1; p += strlen (p) + 1; } reader_table[slot].rdrname = xtrymalloc (strlen (portstr? portstr : list)+1); if (!reader_table[slot].rdrname) { log_error ("error allocating memory for reader name\n"); pcsc_release_context (reader_table[slot].pcsc.context); reader_table[slot].used = 0; return -1; } strcpy (reader_table[slot].rdrname, portstr? portstr : list); xfree (list); list = NULL; reader_table[slot].pcsc.card = 0; reader_table[slot].atrlen = 0; reader_table[slot].last_status = 0; reader_table[slot].connect_card = connect_pcsc_card; reader_table[slot].disconnect_card = disconnect_pcsc_card; reader_table[slot].close_reader = close_pcsc_reader; reader_table[slot].reset_reader = reset_pcsc_reader; reader_table[slot].get_status_reader = pcsc_get_status; reader_table[slot].send_apdu_reader = pcsc_send_apdu; reader_table[slot].dump_status_reader = dump_pcsc_reader_status; dump_reader_status (slot); return slot; } #endif /*!NEED_PCSC_WRAPPER */ /* Open the PC/SC reader using the pcsc_wrapper program. This is needed to cope with different thread models and other peculiarities of libpcsclite. */ #ifdef NEED_PCSC_WRAPPER static int open_pcsc_reader_wrapped (const char *portstr) { int slot; reader_table_t slotp; int fd, rp[2], wp[2]; int n, i; pid_t pid; size_t len; unsigned char msgbuf[9]; int err; unsigned int dummy_status; int sw = SW_HOST_CARD_IO_ERROR; /* Note that we use the constant and not the fucntion because this code won't be be used under Windows. */ const char *wrapperpgm = GNUPG_LIBEXECDIR "/gnupg-pcsc-wrapper"; if (access (wrapperpgm, X_OK)) { log_error ("can't run PC/SC access module `%s': %s\n", wrapperpgm, strerror (errno)); return -1; } slot = new_reader_slot (); if (slot == -1) return -1; slotp = reader_table + slot; /* Fire up the PC/SCc wrapper. We don't use any fork/exec code from the common directy but implement it directly so that this file may still be source copied. */ if (pipe (rp) == -1) { log_error ("error creating a pipe: %s\n", strerror (errno)); slotp->used = 0; return -1; } if (pipe (wp) == -1) { log_error ("error creating a pipe: %s\n", strerror (errno)); close (rp[0]); close (rp[1]); slotp->used = 0; return -1; } pid = fork (); if (pid == -1) { log_error ("error forking process: %s\n", strerror (errno)); close (rp[0]); close (rp[1]); close (wp[0]); close (wp[1]); slotp->used = 0; return -1; } slotp->pcsc.pid = pid; if (!pid) { /* === Child === */ /* Double fork. */ pid = fork (); if (pid == -1) _exit (31); if (pid) _exit (0); /* Immediate exit this parent, so that the child gets cleaned up by the init process. */ /* Connect our pipes. */ if (wp[0] != 0 && dup2 (wp[0], 0) == -1) log_fatal ("dup2 stdin failed: %s\n", strerror (errno)); if (rp[1] != 1 && dup2 (rp[1], 1) == -1) log_fatal ("dup2 stdout failed: %s\n", strerror (errno)); /* Send stderr to the bit bucket. */ fd = open ("/dev/null", O_WRONLY); if (fd == -1) log_fatal ("can't open `/dev/null': %s", strerror (errno)); if (fd != 2 && dup2 (fd, 2) == -1) log_fatal ("dup2 stderr failed: %s\n", strerror (errno)); /* Close all other files. */ close_all_fds (3, NULL); execl (wrapperpgm, "pcsc-wrapper", "--", "1", /* API version */ opt.pcsc_driver, /* Name of the PC/SC library. */ NULL); _exit (31); } /* === Parent === */ close (wp[0]); close (rp[1]); slotp->pcsc.req_fd = wp[1]; slotp->pcsc.rsp_fd = rp[0]; /* Wait for the intermediate child to terminate. */ #ifdef USE_GNU_PTH #define WAIT pth_waitpid #else #define WAIT waitpid #endif while ( (i=WAIT (pid, NULL, 0)) == -1 && errno == EINTR) ; #undef WAIT /* Now send the open request. */ msgbuf[0] = 0x01; /* OPEN command. */ len = portstr? strlen (portstr):0; msgbuf[1] = (len >> 24); msgbuf[2] = (len >> 16); msgbuf[3] = (len >> 8); msgbuf[4] = (len ); if ( writen (slotp->pcsc.req_fd, msgbuf, 5) || (portstr && writen (slotp->pcsc.req_fd, portstr, len))) { log_error ("error sending PC/SC OPEN request: %s\n", strerror (errno)); goto command_failed; } /* Read the response. */ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9) { log_error ("error receiving PC/SC OPEN response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } - len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4]; + len = buf32_to_size_t (msgbuf+1); if (msgbuf[0] != 0x81 || len < 4) { log_error ("invalid response header from PC/SC received\n"); goto command_failed; } len -= 4; /* Already read the error code. */ if (len > DIM (slotp->atr)) { log_error ("PC/SC returned a too large ATR (len=%lx)\n", (unsigned long)len); goto command_failed; } - err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16) - | (msgbuf[7] << 8 ) | msgbuf[8]); + err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5)); + if (err) { log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err)); sw = pcsc_error_to_sw (err); goto command_failed; } slotp->last_status = 0; /* The open request may return a zero for the ATR length to indicate that no card is present. */ n = len; if (n) { if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n) { log_error ("error receiving PC/SC OPEN response: %s\n", i? strerror (errno) : "premature EOF"); goto command_failed; } /* If we got to here we know that a card is present and usable. Thus remember this. */ slotp->last_status = ( APDU_CARD_USABLE | APDU_CARD_PRESENT | APDU_CARD_ACTIVE); } slotp->atrlen = len; reader_table[slot].close_reader = close_pcsc_reader; reader_table[slot].reset_reader = reset_pcsc_reader; reader_table[slot].get_status_reader = pcsc_get_status; reader_table[slot].send_apdu_reader = pcsc_send_apdu; reader_table[slot].dump_status_reader = dump_pcsc_reader_status; /* Read the status so that IS_T0 will be set. */ pcsc_get_status (slot, &dummy_status); dump_reader_status (slot); return slot; command_failed: close (slotp->pcsc.req_fd); close (slotp->pcsc.rsp_fd); slotp->pcsc.req_fd = -1; slotp->pcsc.rsp_fd = -1; kill (slotp->pcsc.pid, SIGTERM); slotp->pcsc.pid = (pid_t)(-1); slotp->used = 0; /* There is no way to return SW. */ return -1; } #endif /*NEED_PCSC_WRAPPER*/ static int open_pcsc_reader (const char *portstr) { #ifdef NEED_PCSC_WRAPPER return open_pcsc_reader_wrapped (portstr); #else return open_pcsc_reader_direct (portstr); #endif } #ifdef HAVE_LIBUSB /* Internal CCID driver interface. */ static void dump_ccid_reader_status (int slot) { log_info ("reader slot %d: using ccid driver\n", slot); } static int close_ccid_reader (int slot) { ccid_close_reader (reader_table[slot].ccid.handle); reader_table[slot].used = 0; return 0; } static int shutdown_ccid_reader (int slot) { ccid_shutdown_reader (reader_table[slot].ccid.handle); return 0; } static int reset_ccid_reader (int slot) { int err; reader_table_t slotp = reader_table + slot; unsigned char atr[33]; size_t atrlen; err = ccid_get_atr (slotp->ccid.handle, atr, sizeof atr, &atrlen); if (err) return err; /* If the reset was successful, update the ATR. */ assert (sizeof slotp->atr >= sizeof atr); slotp->atrlen = atrlen; memcpy (slotp->atr, atr, atrlen); dump_reader_status (slot); return 0; } static int set_progress_cb_ccid_reader (int slot, gcry_handler_progress_t cb, void *cb_arg) { reader_table_t slotp = reader_table + slot; return ccid_set_progress_cb (slotp->ccid.handle, cb, cb_arg); } static int get_status_ccid (int slot, unsigned int *status) { int rc; int bits; rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits); if (rc) return rc; if (bits == 0) *status = (APDU_CARD_USABLE|APDU_CARD_PRESENT|APDU_CARD_ACTIVE); else if (bits == 1) *status = APDU_CARD_PRESENT; else *status = 0; return 0; } /* Actually send the APDU of length APDULEN to SLOT and return a maximum of *BUFLEN data in BUFFER, the actual returned size will be set to BUFLEN. Returns: Internal CCID driver error code. */ static int send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen, unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo) { long err; size_t maxbuflen; /* If we don't have an ATR, we need to reset the reader first. */ if (!reader_table[slot].atrlen && (err = reset_ccid_reader (slot))) return err; if (DBG_CARD_IO) log_printhex (" raw apdu:", apdu, apdulen); maxbuflen = *buflen; if (pininfo) err = ccid_transceive_secure (reader_table[slot].ccid.handle, apdu, apdulen, pininfo->mode, pininfo->minlen, pininfo->maxlen, pininfo->padlen, buffer, maxbuflen, buflen); else err = ccid_transceive (reader_table[slot].ccid.handle, apdu, apdulen, buffer, maxbuflen, buflen); if (err) log_error ("ccid_transceive failed: (0x%lx)\n", err); return err; } /* Check whether the CCID reader supports the ISO command code COMMAND on the keypad. Return 0 on success. For a description of the pin parameters, see ccid-driver.c */ static int check_ccid_keypad (int slot, int command, int pin_mode, int pinlen_min, int pinlen_max, int pin_padlen) { unsigned char apdu[] = { 0, 0, 0, 0x81 }; apdu[1] = command; return ccid_transceive_secure (reader_table[slot].ccid.handle, apdu, sizeof apdu, pin_mode, pinlen_min, pinlen_max, pin_padlen, NULL, 0, NULL); } /* Open the reader and try to read an ATR. */ static int open_ccid_reader (const char *portstr) { int err; int slot; reader_table_t slotp; slot = new_reader_slot (); if (slot == -1) return -1; slotp = reader_table + slot; err = ccid_open_reader (&slotp->ccid.handle, portstr); if (err) { slotp->used = 0; return -1; } err = ccid_get_atr (slotp->ccid.handle, slotp->atr, sizeof slotp->atr, &slotp->atrlen); if (err) { slotp->atrlen = 0; err = 0; } else { /* If we got to here we know that a card is present and usable. Thus remember this. */ reader_table[slot].last_status = (APDU_CARD_USABLE | APDU_CARD_PRESENT | APDU_CARD_ACTIVE); } reader_table[slot].close_reader = close_ccid_reader; reader_table[slot].shutdown_reader = shutdown_ccid_reader; reader_table[slot].reset_reader = reset_ccid_reader; reader_table[slot].get_status_reader = get_status_ccid; reader_table[slot].send_apdu_reader = send_apdu_ccid; reader_table[slot].check_keypad = check_ccid_keypad; reader_table[slot].dump_status_reader = dump_ccid_reader_status; reader_table[slot].set_progress_cb = set_progress_cb_ccid_reader; /* Our CCID reader code does not support T=0 at all, thus reset the flag. */ reader_table[slot].is_t0 = 0; dump_reader_status (slot); return slot; } #endif /* HAVE_LIBUSB */ #ifdef USE_G10CODE_RAPDU /* The Remote APDU Interface. This uses the Remote APDU protocol to contact a reader. The port number is actually an index into the list of ports as returned via the protocol. */ static int rapdu_status_to_sw (int status) { int rc; switch (status) { case RAPDU_STATUS_SUCCESS: rc = 0; break; case RAPDU_STATUS_INVCMD: case RAPDU_STATUS_INVPROT: case RAPDU_STATUS_INVSEQ: case RAPDU_STATUS_INVCOOKIE: case RAPDU_STATUS_INVREADER: rc = SW_HOST_INV_VALUE; break; case RAPDU_STATUS_TIMEOUT: rc = SW_HOST_CARD_IO_ERROR; break; case RAPDU_STATUS_CARDIO: rc = SW_HOST_CARD_IO_ERROR; break; case RAPDU_STATUS_NOCARD: rc = SW_HOST_NO_CARD; break; case RAPDU_STATUS_CARDCHG: rc = SW_HOST_NO_CARD; break; case RAPDU_STATUS_BUSY: rc = SW_HOST_BUSY; break; case RAPDU_STATUS_NEEDRESET: rc = SW_HOST_CARD_INACTIVE; break; default: rc = SW_HOST_GENERAL_ERROR; break; } return rc; } static int close_rapdu_reader (int slot) { rapdu_release (reader_table[slot].rapdu.handle); reader_table[slot].used = 0; return 0; } static int reset_rapdu_reader (int slot) { int err; reader_table_t slotp; rapdu_msg_t msg = NULL; slotp = reader_table + slot; err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_RESET); if (err) { log_error ("sending rapdu command RESET failed: %s\n", err < 0 ? strerror (errno): rapdu_strerror (err)); rapdu_msg_release (msg); return rapdu_status_to_sw (err); } err = rapdu_read_msg (slotp->rapdu.handle, &msg); if (err) { log_error ("receiving rapdu message failed: %s\n", err < 0 ? strerror (errno): rapdu_strerror (err)); rapdu_msg_release (msg); return rapdu_status_to_sw (err); } if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen) { int sw = rapdu_status_to_sw (msg->cmd); log_error ("rapdu command RESET failed: %s\n", rapdu_strerror (msg->cmd)); rapdu_msg_release (msg); return sw; } if (msg->datalen > DIM (slotp->atr)) { log_error ("ATR returned by the RAPDU layer is too large\n"); rapdu_msg_release (msg); return SW_HOST_INV_VALUE; } slotp->atrlen = msg->datalen; memcpy (slotp->atr, msg->data, msg->datalen); rapdu_msg_release (msg); return 0; } static int my_rapdu_get_status (int slot, unsigned int *status) { int err; reader_table_t slotp; rapdu_msg_t msg = NULL; int oldslot; slotp = reader_table + slot; oldslot = rapdu_set_reader (slotp->rapdu.handle, slot); err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_GET_STATUS); rapdu_set_reader (slotp->rapdu.handle, oldslot); if (err) { log_error ("sending rapdu command GET_STATUS failed: %s\n", err < 0 ? strerror (errno): rapdu_strerror (err)); return rapdu_status_to_sw (err); } err = rapdu_read_msg (slotp->rapdu.handle, &msg); if (err) { log_error ("receiving rapdu message failed: %s\n", err < 0 ? strerror (errno): rapdu_strerror (err)); rapdu_msg_release (msg); return rapdu_status_to_sw (err); } if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen) { int sw = rapdu_status_to_sw (msg->cmd); log_error ("rapdu command GET_STATUS failed: %s\n", rapdu_strerror (msg->cmd)); rapdu_msg_release (msg); return sw; } *status = msg->data[0]; rapdu_msg_release (msg); return 0; } /* Actually send the APDU of length APDULEN to SLOT and return a maximum of *BUFLEN data in BUFFER, the actual returned size will be set to BUFLEN. Returns: APDU error code. */ static int my_rapdu_send_apdu (int slot, unsigned char *apdu, size_t apdulen, unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo) { int err; reader_table_t slotp; rapdu_msg_t msg = NULL; size_t maxlen = *buflen; slotp = reader_table + slot; *buflen = 0; if (DBG_CARD_IO) log_printhex (" APDU_data:", apdu, apdulen); if (apdulen < 4) { log_error ("rapdu_send_apdu: APDU is too short\n"); return SW_HOST_INV_VALUE; } err = rapdu_send_apdu (slotp->rapdu.handle, apdu, apdulen); if (err) { log_error ("sending rapdu command APDU failed: %s\n", err < 0 ? strerror (errno): rapdu_strerror (err)); rapdu_msg_release (msg); return rapdu_status_to_sw (err); } err = rapdu_read_msg (slotp->rapdu.handle, &msg); if (err) { log_error ("receiving rapdu message failed: %s\n", err < 0 ? strerror (errno): rapdu_strerror (err)); rapdu_msg_release (msg); return rapdu_status_to_sw (err); } if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen) { int sw = rapdu_status_to_sw (msg->cmd); log_error ("rapdu command APDU failed: %s\n", rapdu_strerror (msg->cmd)); rapdu_msg_release (msg); return sw; } if (msg->datalen > maxlen) { log_error ("rapdu response apdu too large\n"); rapdu_msg_release (msg); return SW_HOST_INV_VALUE; } *buflen = msg->datalen; memcpy (buffer, msg->data, msg->datalen); rapdu_msg_release (msg); return 0; } static int open_rapdu_reader (int portno, const unsigned char *cookie, size_t length, int (*readfnc) (void *opaque, void *buffer, size_t size), void *readfnc_value, int (*writefnc) (void *opaque, const void *buffer, size_t size), void *writefnc_value, void (*closefnc) (void *opaque), void *closefnc_value) { int err; int slot; reader_table_t slotp; rapdu_msg_t msg = NULL; slot = new_reader_slot (); if (slot == -1) return -1; slotp = reader_table + slot; slotp->rapdu.handle = rapdu_new (); if (!slotp->rapdu.handle) { slotp->used = 0; return -1; } rapdu_set_reader (slotp->rapdu.handle, portno); rapdu_set_iofunc (slotp->rapdu.handle, readfnc, readfnc_value, writefnc, writefnc_value, closefnc, closefnc_value); rapdu_set_cookie (slotp->rapdu.handle, cookie, length); /* First try to get the current ATR, but if the card is inactive issue a reset instead. */ err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_GET_ATR); if (err == RAPDU_STATUS_NEEDRESET) err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_RESET); if (err) { log_info ("sending rapdu command GET_ATR/RESET failed: %s\n", err < 0 ? strerror (errno): rapdu_strerror (err)); goto failure; } err = rapdu_read_msg (slotp->rapdu.handle, &msg); if (err) { log_info ("receiving rapdu message failed: %s\n", err < 0 ? strerror (errno): rapdu_strerror (err)); goto failure; } if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen) { log_info ("rapdu command GET ATR failed: %s\n", rapdu_strerror (msg->cmd)); goto failure; } if (msg->datalen > DIM (slotp->atr)) { log_error ("ATR returned by the RAPDU layer is too large\n"); goto failure; } slotp->atrlen = msg->datalen; memcpy (slotp->atr, msg->data, msg->datalen); reader_table[slot].close_reader = close_rapdu_reader; reader_table[slot].reset_reader = reset_rapdu_reader; reader_table[slot].get_status_reader = my_rapdu_get_status; reader_table[slot].send_apdu_reader = my_rapdu_send_apdu; reader_table[slot].check_keypad = NULL; reader_table[slot].dump_status_reader = NULL; dump_reader_status (slot); rapdu_msg_release (msg); return slot; failure: rapdu_msg_release (msg); rapdu_release (slotp->rapdu.handle); slotp->used = 0; return -1; } #endif /*USE_G10CODE_RAPDU*/ /* Driver Access */ static int lock_slot (int slot) { #ifdef USE_GNU_PTH if (!pth_mutex_acquire (&reader_table[slot].lock, 0, NULL)) { log_error ("failed to acquire apdu lock: %s\n", strerror (errno)); return SW_HOST_LOCKING_FAILED; } #endif /*USE_GNU_PTH*/ return 0; } static int trylock_slot (int slot) { #ifdef USE_GNU_PTH if (!pth_mutex_acquire (&reader_table[slot].lock, TRUE, NULL)) { if (errno == EBUSY) return SW_HOST_BUSY; log_error ("failed to acquire apdu lock: %s\n", strerror (errno)); return SW_HOST_LOCKING_FAILED; } #endif /*USE_GNU_PTH*/ return 0; } static void unlock_slot (int slot) { #ifdef USE_GNU_PTH if (!pth_mutex_release (&reader_table[slot].lock)) log_error ("failed to release apdu lock: %s\n", strerror (errno)); #endif /*USE_GNU_PTH*/ } /* Open the reader and return an internal slot number or -1 on error. If PORTSTR is NULL we default to a suitable port (for ctAPI: the first USB reader. For PC/SC the first listed reader). */ int apdu_open_reader (const char *portstr) { static int pcsc_api_loaded, ct_api_loaded; #ifdef HAVE_LIBUSB if (!opt.disable_ccid) { int slot, i; const char *s; slot = open_ccid_reader (portstr); if (slot != -1) return slot; /* got one */ /* If a CCID reader specification has been given, the user does not want a fallback to other drivers. */ if (portstr) for (s=portstr, i=0; *s; s++) if (*s == ':' && (++i == 3)) return -1; } #endif /* HAVE_LIBUSB */ if (opt.ctapi_driver && *opt.ctapi_driver) { int port = portstr? atoi (portstr) : 32768; if (!ct_api_loaded) { void *handle; handle = dlopen (opt.ctapi_driver, RTLD_LAZY); if (!handle) { log_error ("apdu_open_reader: failed to open driver: %s\n", dlerror ()); return -1; } CT_init = dlsym (handle, "CT_init"); CT_data = dlsym (handle, "CT_data"); CT_close = dlsym (handle, "CT_close"); if (!CT_init || !CT_data || !CT_close) { log_error ("apdu_open_reader: invalid CT-API driver\n"); dlclose (handle); return -1; } ct_api_loaded = 1; } return open_ct_reader (port); } /* No ctAPI configured, so lets try the PC/SC API */ if (!pcsc_api_loaded) { #ifndef NEED_PCSC_WRAPPER void *handle; handle = dlopen (opt.pcsc_driver, RTLD_LAZY); if (!handle) { log_error ("apdu_open_reader: failed to open driver `%s': %s\n", opt.pcsc_driver, dlerror ()); return -1; } pcsc_establish_context = dlsym (handle, "SCardEstablishContext"); pcsc_release_context = dlsym (handle, "SCardReleaseContext"); pcsc_list_readers = dlsym (handle, "SCardListReaders"); #if defined(_WIN32) || defined(__CYGWIN__) if (!pcsc_list_readers) pcsc_list_readers = dlsym (handle, "SCardListReadersA"); #endif pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange"); #if defined(_WIN32) || defined(__CYGWIN__) if (!pcsc_get_status_change) pcsc_get_status_change = dlsym (handle, "SCardGetStatusChangeA"); #endif pcsc_connect = dlsym (handle, "SCardConnect"); #if defined(_WIN32) || defined(__CYGWIN__) if (!pcsc_connect) pcsc_connect = dlsym (handle, "SCardConnectA"); #endif pcsc_reconnect = dlsym (handle, "SCardReconnect"); #if defined(_WIN32) || defined(__CYGWIN__) if (!pcsc_reconnect) pcsc_reconnect = dlsym (handle, "SCardReconnectA"); #endif pcsc_disconnect = dlsym (handle, "SCardDisconnect"); pcsc_status = dlsym (handle, "SCardStatus"); #if defined(_WIN32) || defined(__CYGWIN__) if (!pcsc_status) pcsc_status = dlsym (handle, "SCardStatusA"); #endif pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction"); pcsc_end_transaction = dlsym (handle, "SCardEndTransaction"); pcsc_transmit = dlsym (handle, "SCardTransmit"); pcsc_set_timeout = dlsym (handle, "SCardSetTimeout"); if (!pcsc_establish_context || !pcsc_release_context || !pcsc_list_readers || !pcsc_get_status_change || !pcsc_connect || !pcsc_reconnect || !pcsc_disconnect || !pcsc_status || !pcsc_begin_transaction || !pcsc_end_transaction || !pcsc_transmit /* || !pcsc_set_timeout */) { /* Note that set_timeout is currently not used and also not available under Windows. */ log_error ("apdu_open_reader: invalid PC/SC driver " "(%d%d%d%d%d%d%d%d%d%d%d%d)\n", !!pcsc_establish_context, !!pcsc_release_context, !!pcsc_list_readers, !!pcsc_get_status_change, !!pcsc_connect, !!pcsc_reconnect, !!pcsc_disconnect, !!pcsc_status, !!pcsc_begin_transaction, !!pcsc_end_transaction, !!pcsc_transmit, !!pcsc_set_timeout ); dlclose (handle); return -1; } #endif /*!NEED_PCSC_WRAPPER*/ pcsc_api_loaded = 1; } return open_pcsc_reader (portstr); } /* Open an remote reader and return an internal slot number or -1 on error. This function is an alternative to apdu_open_reader and used with remote readers only. Note that the supplied CLOSEFNC will only be called once and the slot will not be valid afther this. If PORTSTR is NULL we default to the first availabe port. */ int apdu_open_remote_reader (const char *portstr, const unsigned char *cookie, size_t length, int (*readfnc) (void *opaque, void *buffer, size_t size), void *readfnc_value, int (*writefnc) (void *opaque, const void *buffer, size_t size), void *writefnc_value, void (*closefnc) (void *opaque), void *closefnc_value) { #ifdef USE_G10CODE_RAPDU return open_rapdu_reader (portstr? atoi (portstr) : 0, cookie, length, readfnc, readfnc_value, writefnc, writefnc_value, closefnc, closefnc_value); #else (void)portstr; (void)cookie; (void)length; (void)readfnc; (void)readfnc_value; (void)writefnc; (void)writefnc_value; (void)closefnc; (void)closefnc_value; #ifdef _WIN32 errno = ENOENT; #else errno = ENOSYS; #endif return -1; #endif } int apdu_close_reader (int slot) { int sw; if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; sw = apdu_disconnect (slot); if (sw) return sw; if (reader_table[slot].close_reader) return reader_table[slot].close_reader (slot); return SW_HOST_NOT_SUPPORTED; } /* Function suitable for a cleanup function to close all reader. It should not be used if the reader will be opened again. The reason for implementing this to properly close USB devices so that they will startup the next time without error. */ void apdu_prepare_exit (void) { static int sentinel; int slot; if (!sentinel) { sentinel = 1; for (slot = 0; slot < MAX_READER; slot++) if (reader_table[slot].used) { apdu_disconnect (slot); if (reader_table[slot].close_reader) reader_table[slot].close_reader (slot); reader_table[slot].used = 0; } sentinel = 0; } } /* Shutdown a reader; that is basically the same as a close but keeps the handle ready for later use. A apdu_reset_reader or apdu_connect should be used to get it active again. */ int apdu_shutdown_reader (int slot) { int sw; if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; sw = apdu_disconnect (slot); if (sw) return sw; if (reader_table[slot].shutdown_reader) return reader_table[slot].shutdown_reader (slot); return SW_HOST_NOT_SUPPORTED; } /* Enumerate all readers and return information on whether this reader is in use. The caller should start with SLOT set to 0 and increment it with each call until an error is returned. */ int apdu_enum_reader (int slot, int *used) { if (slot < 0 || slot >= MAX_READER) return SW_HOST_NO_DRIVER; *used = reader_table[slot].used; return 0; } /* Connect a card. This is used to power up the card and make sure that an ATR is available. */ int apdu_connect (int slot) { int sw; if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; /* Only if the access method provides a connect function we use it. If not, we expect that the card has been implicitly connected by apdu_open_reader. */ if (reader_table[slot].connect_card) { sw = lock_slot (slot); if (!sw) { sw = reader_table[slot].connect_card (slot); unlock_slot (slot); } } else sw = 0; /* We need to call apdu_get_status_internal, so that the last-status machinery gets setup properly even if a card is inserted while scdaemon is fired up and apdu_get_status has not yet been called. Without that we would force a reset of the card with the next call to apdu_get_status. */ apdu_get_status_internal (slot, 1, 1, NULL, NULL); return sw; } int apdu_disconnect (int slot) { int sw; if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; if (reader_table[slot].disconnect_card) { sw = lock_slot (slot); if (!sw) { sw = reader_table[slot].disconnect_card (slot); unlock_slot (slot); } } else sw = 0; return sw; } /* Set the progress callback of SLOT to CB and its args to CB_ARG. If CB is NULL the progress callback is removed. */ int apdu_set_progress_cb (int slot, gcry_handler_progress_t cb, void *cb_arg) { int sw; if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; if (reader_table[slot].set_progress_cb) { sw = lock_slot (slot); if (!sw) { sw = reader_table[slot].set_progress_cb (slot, cb, cb_arg); unlock_slot (slot); } } else sw = 0; return sw; } /* Do a reset for the card in reader at SLOT. */ int apdu_reset (int slot) { int sw; if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; if ((sw = lock_slot (slot))) return sw; reader_table[slot].last_status = 0; if (reader_table[slot].reset_reader) sw = reader_table[slot].reset_reader (slot); if (!sw) { /* If we got to here we know that a card is present and usable. Thus remember this. */ reader_table[slot].last_status = (APDU_CARD_USABLE | APDU_CARD_PRESENT | APDU_CARD_ACTIVE); } unlock_slot (slot); return sw; } /* Activate a card if it has not yet been done. This is a kind of reset-if-required. It is useful to test for presence of a card before issuing a bunch of apdu commands. It does not wait on a locked card. */ int apdu_activate (int slot) { int sw; unsigned int s; if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; if ((sw = trylock_slot (slot))) return sw; if (reader_table[slot].get_status_reader) sw = reader_table[slot].get_status_reader (slot, &s); if (!sw) { if (!(s & 2)) /* Card not present. */ sw = SW_HOST_NO_CARD; else if ( ((s & 2) && !(s & 4)) || !reader_table[slot].atrlen ) { /* We don't have an ATR or a card is present though inactive: do a reset now. */ if (reader_table[slot].reset_reader) { reader_table[slot].last_status = 0; sw = reader_table[slot].reset_reader (slot); if (!sw) { /* If we got to here we know that a card is present and usable. Thus remember this. */ reader_table[slot].last_status = (APDU_CARD_USABLE | APDU_CARD_PRESENT | APDU_CARD_ACTIVE); } } } } unlock_slot (slot); return sw; } unsigned char * apdu_get_atr (int slot, size_t *atrlen) { unsigned char *buf; if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return NULL; if (!reader_table[slot].atrlen) return NULL; buf = xtrymalloc (reader_table[slot].atrlen); if (!buf) return NULL; memcpy (buf, reader_table[slot].atr, reader_table[slot].atrlen); *atrlen = reader_table[slot].atrlen; return buf; } /* Retrieve the status for SLOT. The function does only wait for the card to become available if HANG is set to true. On success the bits in STATUS will be set to APDU_CARD_USABLE (bit 0) = card present and usable APDU_CARD_PRESENT (bit 1) = card present APDU_CARD_ACTIVE (bit 2) = card active (bit 3) = card access locked [not yet implemented] For must applications, testing bit 0 is sufficient. CHANGED will receive the value of the counter tracking the number of card insertions. This value may be used to detect a card change. */ static int apdu_get_status_internal (int slot, int hang, int no_atr_reset, unsigned int *status, unsigned int *changed) { int sw; unsigned int s; if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; if ((sw = hang? lock_slot (slot) : trylock_slot (slot))) return sw; if (reader_table[slot].get_status_reader) sw = reader_table[slot].get_status_reader (slot, &s); unlock_slot (slot); if (sw) { reader_table[slot].last_status = 0; return sw; } /* Keep track of changes. */ if (s != reader_table[slot].last_status || !reader_table[slot].any_status ) { reader_table[slot].change_counter++; /* Make sure that the ATR is invalid so that a reset will be triggered by apdu_activate. */ if (!no_atr_reset) reader_table[slot].atrlen = 0; } reader_table[slot].any_status = 1; reader_table[slot].last_status = s; if (status) *status = s; if (changed) *changed = reader_table[slot].change_counter; return 0; } /* See above for a description. */ int apdu_get_status (int slot, int hang, unsigned int *status, unsigned int *changed) { return apdu_get_status_internal (slot, hang, 0, status, changed); } /* Check whether the reader supports the ISO command code COMMAND on the keypad. Return 0 on success. For a description of the pin parameters, see ccid-driver.c */ int apdu_check_keypad (int slot, int command, int pin_mode, int pinlen_min, int pinlen_max, int pin_padlen) { if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; if (reader_table[slot].check_keypad) return reader_table[slot].check_keypad (slot, command, pin_mode, pinlen_min, pinlen_max, pin_padlen); else return SW_HOST_NOT_SUPPORTED; } /* Dispatcher for the actual send_apdu function. Note, that this function should be called in locked state. */ static int send_apdu (int slot, unsigned char *apdu, size_t apdulen, unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo) { if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; if (reader_table[slot].send_apdu_reader) return reader_table[slot].send_apdu_reader (slot, apdu, apdulen, buffer, buflen, pininfo); else return SW_HOST_NOT_SUPPORTED; } /* Core APDU tranceiver function. Parameters are described at apdu_send_le with the exception of PININFO which indicates keypad related operations if not NULL. If EXTENDED_MODE is not 0 command chaining or extended length will be used according to these values: n < 0 := Use command chaining with the data part limited to -n in each chunk. If -1 is used a default value is used. n == 0 := No extended mode or command chaining. n == 1 := Use extended length for input and output without a length limit. n > 1 := Use extended length with up to N bytes. */ static int send_le (int slot, int class, int ins, int p0, int p1, int lc, const char *data, int le, unsigned char **retbuf, size_t *retbuflen, struct pininfo_s *pininfo, int extended_mode) { #define SHORT_RESULT_BUFFER_SIZE 258 /* We allocate 8 extra bytes as a safety margin towards a driver bug. */ unsigned char short_result_buffer[SHORT_RESULT_BUFFER_SIZE+10]; unsigned char *result_buffer = NULL; size_t result_buffer_size; unsigned char *result; size_t resultlen; unsigned char short_apdu_buffer[5+256+1]; unsigned char *apdu_buffer = NULL; size_t apdu_buffer_size; unsigned char *apdu; size_t apdulen; int sw; long rc; /* We need a long here due to PC/SC. */ int did_exact_length_hack = 0; int use_chaining = 0; int use_extended_length = 0; int lc_chunk; if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; if (DBG_CARD_IO) log_debug ("send apdu: c=%02X i=%02X p1=%02X p2=%02X lc=%d le=%d em=%d\n", class, ins, p0, p1, lc, le, extended_mode); if (lc != -1 && (lc > 255 || lc < 0)) { /* Data does not fit into an APDU. What we do now depends on the EXTENDED_MODE parameter. */ if (!extended_mode) return SW_WRONG_LENGTH; /* No way to send such an APDU. */ else if (extended_mode > 0) use_extended_length = 1; else if (extended_mode < 0) { /* Send APDU using chaining mode. */ if (lc > 16384) return SW_WRONG_LENGTH; /* Sanity check. */ if ((class&0xf0) != 0) return SW_HOST_INV_VALUE; /* Upper 4 bits need to be 0. */ use_chaining = extended_mode == -1? 255 : -extended_mode; use_chaining &= 0xff; } else return SW_HOST_INV_VALUE; } else if (lc == -1 && extended_mode > 0) use_extended_length = 1; if (le != -1 && (le > (extended_mode > 0? 255:256) || le < 0)) { /* Expected Data does not fit into an APDU. What we do now depends on the EXTENDED_MODE parameter. Note that a check for command chaining does not make sense because we are looking at Le. */ if (!extended_mode) return SW_WRONG_LENGTH; /* No way to send such an APDU. */ else if (use_extended_length) ; /* We are already using extended length. */ else if (extended_mode > 0) use_extended_length = 1; else return SW_HOST_INV_VALUE; } if ((!data && lc != -1) || (data && lc == -1)) return SW_HOST_INV_VALUE; if (use_extended_length) { if (reader_table[slot].is_t0) return SW_HOST_NOT_SUPPORTED; /* Space for: cls/ins/p1/p2+Z+2_byte_Lc+Lc+2_byte_Le. */ apdu_buffer_size = 4 + 1 + (lc >= 0? (2+lc):0) + 2; apdu_buffer = xtrymalloc (apdu_buffer_size + 10); if (!apdu_buffer) return SW_HOST_OUT_OF_CORE; apdu = apdu_buffer; } else { apdu_buffer_size = sizeof short_apdu_buffer; apdu = short_apdu_buffer; } if (use_extended_length && (le > 256 || le < 0)) { result_buffer_size = le < 0? 4096 : le; result_buffer = xtrymalloc (result_buffer_size + 10); if (!result_buffer) { xfree (apdu_buffer); return SW_HOST_OUT_OF_CORE; } result = result_buffer; } else { result_buffer_size = SHORT_RESULT_BUFFER_SIZE; result = short_result_buffer; } #undef SHORT_RESULT_BUFFER_SIZE if ((sw = lock_slot (slot))) { xfree (apdu_buffer); xfree (result_buffer); return sw; } do { if (use_extended_length) { use_chaining = 0; apdulen = 0; apdu[apdulen++] = class; apdu[apdulen++] = ins; apdu[apdulen++] = p0; apdu[apdulen++] = p1; apdu[apdulen++] = 0; /* Z byte: Extended length marker. */ if (lc >= 0) { apdu[apdulen++] = ((lc >> 8) & 0xff); apdu[apdulen++] = (lc & 0xff); memcpy (apdu+apdulen, data, lc); data += lc; apdulen += lc; } if (le != -1) { apdu[apdulen++] = ((le >> 8) & 0xff); apdu[apdulen++] = (le & 0xff); } } else { apdulen = 0; apdu[apdulen] = class; if (use_chaining && lc > 255) { apdu[apdulen] |= 0x10; assert (use_chaining < 256); lc_chunk = use_chaining; lc -= use_chaining; } else { use_chaining = 0; lc_chunk = lc; } apdulen++; apdu[apdulen++] = ins; apdu[apdulen++] = p0; apdu[apdulen++] = p1; if (lc_chunk != -1) { apdu[apdulen++] = lc_chunk; memcpy (apdu+apdulen, data, lc_chunk); data += lc_chunk; apdulen += lc_chunk; /* T=0 does not allow the use of Lc together with Le; thus disable Le in this case. */ if (reader_table[slot].is_t0) le = -1; } if (le != -1 && !use_chaining) apdu[apdulen++] = le; /* Truncation is okay (0 means 256). */ } exact_length_hack: /* As a safeguard don't pass any garbage to the driver. */ assert (apdulen <= apdu_buffer_size); memset (apdu+apdulen, 0, apdu_buffer_size - apdulen); resultlen = result_buffer_size; rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo); if (rc || resultlen < 2) { log_info ("apdu_send_simple(%d) failed: %s\n", slot, apdu_strerror (rc)); unlock_slot (slot); xfree (apdu_buffer); xfree (result_buffer); return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE; } sw = (result[resultlen-2] << 8) | result[resultlen-1]; if (!use_extended_length && !did_exact_length_hack && SW_EXACT_LENGTH_P (sw)) { apdu[apdulen-1] = (sw & 0x00ff); did_exact_length_hack = 1; goto exact_length_hack; } } while (use_chaining && sw == SW_SUCCESS); if (apdu_buffer) { xfree (apdu_buffer); apdu_buffer = NULL; apdu_buffer_size = 0; } /* Store away the returned data but strip the statusword. */ resultlen -= 2; if (DBG_CARD_IO) { log_debug (" response: sw=%04X datalen=%d\n", sw, (unsigned int)resultlen); if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA)) log_printhex (" dump: ", result, resultlen); } if (sw == SW_SUCCESS || sw == SW_EOF_REACHED) { if (retbuf) { *retbuf = xtrymalloc (resultlen? resultlen : 1); if (!*retbuf) { unlock_slot (slot); xfree (result_buffer); return SW_HOST_OUT_OF_CORE; } *retbuflen = resultlen; memcpy (*retbuf, result, resultlen); } } else if ((sw & 0xff00) == SW_MORE_DATA) { unsigned char *p = NULL, *tmp; size_t bufsize = 4096; /* It is likely that we need to return much more data, so we start off with a large buffer. */ if (retbuf) { *retbuf = p = xtrymalloc (bufsize); if (!*retbuf) { unlock_slot (slot); xfree (result_buffer); return SW_HOST_OUT_OF_CORE; } assert (resultlen < bufsize); memcpy (p, result, resultlen); p += resultlen; } do { int len = (sw & 0x00ff); if (DBG_CARD_IO) log_debug ("apdu_send_simple(%d): %d more bytes available\n", slot, len); apdu_buffer_size = sizeof short_apdu_buffer; apdu = short_apdu_buffer; apdulen = 0; apdu[apdulen++] = class; apdu[apdulen++] = 0xC0; apdu[apdulen++] = 0; apdu[apdulen++] = 0; apdu[apdulen++] = len; assert (apdulen <= apdu_buffer_size); memset (apdu+apdulen, 0, apdu_buffer_size - apdulen); resultlen = result_buffer_size; rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL); if (rc || resultlen < 2) { log_error ("apdu_send_simple(%d) for get response failed: %s\n", slot, apdu_strerror (rc)); unlock_slot (slot); xfree (result_buffer); return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE; } sw = (result[resultlen-2] << 8) | result[resultlen-1]; resultlen -= 2; if (DBG_CARD_IO) { log_debug (" more: sw=%04X datalen=%d\n", sw, (unsigned int)resultlen); if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA)) log_printhex (" dump: ", result, resultlen); } if ((sw & 0xff00) == SW_MORE_DATA || sw == SW_SUCCESS || sw == SW_EOF_REACHED ) { if (retbuf && resultlen) { if (p - *retbuf + resultlen > bufsize) { bufsize += resultlen > 4096? resultlen: 4096; tmp = xtryrealloc (*retbuf, bufsize); if (!tmp) { unlock_slot (slot); xfree (result_buffer); return SW_HOST_OUT_OF_CORE; } p = tmp + (p - *retbuf); *retbuf = tmp; } memcpy (p, result, resultlen); p += resultlen; } } else log_info ("apdu_send_simple(%d) " "got unexpected status %04X from get response\n", slot, sw); } while ((sw & 0xff00) == SW_MORE_DATA); if (retbuf) { *retbuflen = p - *retbuf; tmp = xtryrealloc (*retbuf, *retbuflen); if (tmp) *retbuf = tmp; } } unlock_slot (slot); xfree (result_buffer); if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS) log_printhex (" dump: ", *retbuf, *retbuflen); return sw; } /* Send an APDU to the card in SLOT. The APDU is created from all given parameters: CLASS, INS, P0, P1, LC, DATA, LE. A value of -1 for LC won't sent this field and the data field; in this case DATA must also be passed as NULL. If EXTENDED_MODE is not 0 command chaining or extended length will be used; see send_le for details. The return value is the status word or -1 for an invalid SLOT or other non card related error. If RETBUF is not NULL, it will receive an allocated buffer with the returned data. The length of that data will be put into *RETBUFLEN. The caller is reponsible for releasing the buffer even in case of errors. */ int apdu_send_le(int slot, int extended_mode, int class, int ins, int p0, int p1, int lc, const char *data, int le, unsigned char **retbuf, size_t *retbuflen) { return send_le (slot, class, ins, p0, p1, lc, data, le, retbuf, retbuflen, NULL, extended_mode); } /* Send an APDU to the card in SLOT. The APDU is created from all given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for LC won't sent this field and the data field; in this case DATA must also be passed as NULL. If EXTENDED_MODE is not 0 command chaining or extended length will be used; see send_le for details. The return value is the status word or -1 for an invalid SLOT or other non card related error. If RETBUF is not NULL, it will receive an allocated buffer with the returned data. The length of that data will be put into *RETBUFLEN. The caller is reponsible for releasing the buffer even in case of errors. */ int apdu_send (int slot, int extended_mode, int class, int ins, int p0, int p1, int lc, const char *data, unsigned char **retbuf, size_t *retbuflen) { return send_le (slot, class, ins, p0, p1, lc, data, 256, retbuf, retbuflen, NULL, extended_mode); } /* Send an APDU to the card in SLOT. The APDU is created from all given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for LC won't sent this field and the data field; in this case DATA must also be passed as NULL. If EXTENDED_MODE is not 0 command chaining or extended length will be used; see send_le for details. The return value is the status word or -1 for an invalid SLOT or other non card related error. No data will be returned. */ int apdu_send_simple (int slot, int extended_mode, int class, int ins, int p0, int p1, int lc, const char *data) { return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL, extended_mode); } /* Same as apdu_send_simple but uses the keypad of the reader. */ int apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1, int lc, const char *data, int pin_mode, int pinlen_min, int pinlen_max, int pin_padlen) { struct pininfo_s pininfo; pininfo.mode = pin_mode; pininfo.minlen = pinlen_min; pininfo.maxlen = pinlen_max; pininfo.padlen = pin_padlen; return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, &pininfo, 0); } /* This is a more generic version of the apdu sending routine. It takes an already formatted APDU in APDUDATA or length APDUDATALEN and returns with an APDU including the status word. With HANDLE_MORE set to true this function will handle the MORE DATA status and return all APDUs concatenated with one status word at the end. If EXTENDED_LENGTH is != 0 extended lengths are allowed with a max. result data length of EXTENDED_LENGTH bytes. The function does not return a regular status word but 0 on success. If the slot is locked, the function returns immediately with an error. */ int apdu_send_direct (int slot, size_t extended_length, const unsigned char *apdudata, size_t apdudatalen, int handle_more, unsigned char **retbuf, size_t *retbuflen) { #define SHORT_RESULT_BUFFER_SIZE 258 unsigned char short_result_buffer[SHORT_RESULT_BUFFER_SIZE+10]; unsigned char *result_buffer = NULL; size_t result_buffer_size; unsigned char *result; size_t resultlen; unsigned char short_apdu_buffer[5+256+10]; unsigned char *apdu_buffer = NULL; unsigned char *apdu; size_t apdulen; int sw; long rc; /* we need a long here due to PC/SC. */ int class; if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; if (apdudatalen > 65535) return SW_HOST_INV_VALUE; if (apdudatalen > sizeof short_apdu_buffer - 5) { apdu_buffer = xtrymalloc (apdudatalen + 5); if (!apdu_buffer) return SW_HOST_OUT_OF_CORE; apdu = apdu_buffer; } else { apdu = short_apdu_buffer; } apdulen = apdudatalen; memcpy (apdu, apdudata, apdudatalen); class = apdulen? *apdu : 0; if (extended_length >= 256 && extended_length <= 65536) { result_buffer_size = extended_length; result_buffer = xtrymalloc (result_buffer_size + 10); if (!result_buffer) { xfree (apdu_buffer); return SW_HOST_OUT_OF_CORE; } result = result_buffer; } else { result_buffer_size = SHORT_RESULT_BUFFER_SIZE; result = short_result_buffer; } #undef SHORT_RESULT_BUFFER_SIZE if ((sw = trylock_slot (slot))) { xfree (apdu_buffer); xfree (result_buffer); return sw; } resultlen = result_buffer_size; rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL); xfree (apdu_buffer); apdu_buffer = NULL; if (rc || resultlen < 2) { log_error ("apdu_send_direct(%d) failed: %s\n", slot, apdu_strerror (rc)); unlock_slot (slot); xfree (result_buffer); return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE; } sw = (result[resultlen-2] << 8) | result[resultlen-1]; /* Store away the returned data but strip the statusword. */ resultlen -= 2; if (DBG_CARD_IO) { log_debug (" response: sw=%04X datalen=%d\n", sw, (unsigned int)resultlen); if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA)) log_printhex (" dump: ", result, resultlen); } if (handle_more && (sw & 0xff00) == SW_MORE_DATA) { unsigned char *p = NULL, *tmp; size_t bufsize = 4096; /* It is likely that we need to return much more data, so we start off with a large buffer. */ if (retbuf) { *retbuf = p = xtrymalloc (bufsize + 2); if (!*retbuf) { unlock_slot (slot); xfree (result_buffer); return SW_HOST_OUT_OF_CORE; } assert (resultlen < bufsize); memcpy (p, result, resultlen); p += resultlen; } do { int len = (sw & 0x00ff); if (DBG_CARD_IO) log_debug ("apdu_send_direct(%d): %d more bytes available\n", slot, len); apdu = short_apdu_buffer; apdulen = 0; apdu[apdulen++] = class; apdu[apdulen++] = 0xC0; apdu[apdulen++] = 0; apdu[apdulen++] = 0; apdu[apdulen++] = len; memset (apdu+apdulen, 0, sizeof (short_apdu_buffer) - apdulen); resultlen = result_buffer_size; rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL); if (rc || resultlen < 2) { log_error ("apdu_send_direct(%d) for get response failed: %s\n", slot, apdu_strerror (rc)); unlock_slot (slot); xfree (result_buffer); return rc ? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE; } sw = (result[resultlen-2] << 8) | result[resultlen-1]; resultlen -= 2; if (DBG_CARD_IO) { log_debug (" more: sw=%04X datalen=%d\n", sw, (unsigned int)resultlen); if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA)) log_printhex (" dump: ", result, resultlen); } if ((sw & 0xff00) == SW_MORE_DATA || sw == SW_SUCCESS || sw == SW_EOF_REACHED ) { if (retbuf && resultlen) { if (p - *retbuf + resultlen > bufsize) { bufsize += resultlen > 4096? resultlen: 4096; tmp = xtryrealloc (*retbuf, bufsize + 2); if (!tmp) { unlock_slot (slot); xfree (result_buffer); return SW_HOST_OUT_OF_CORE; } p = tmp + (p - *retbuf); *retbuf = tmp; } memcpy (p, result, resultlen); p += resultlen; } } else log_info ("apdu_send_direct(%d) " "got unexpected status %04X from get response\n", slot, sw); } while ((sw & 0xff00) == SW_MORE_DATA); if (retbuf) { *retbuflen = p - *retbuf; tmp = xtryrealloc (*retbuf, *retbuflen + 2); if (tmp) *retbuf = tmp; } } else { if (retbuf) { *retbuf = xtrymalloc ((resultlen? resultlen : 1)+2); if (!*retbuf) { unlock_slot (slot); xfree (result_buffer); return SW_HOST_OUT_OF_CORE; } *retbuflen = resultlen; memcpy (*retbuf, result, resultlen); } } unlock_slot (slot); xfree (result_buffer); /* Append the status word. Note that we reserved the two extra bytes while allocating the buffer. */ if (retbuf) { (*retbuf)[(*retbuflen)++] = (sw >> 8); (*retbuf)[(*retbuflen)++] = sw; } if (DBG_CARD_IO && retbuf) log_printhex (" dump: ", *retbuf, *retbuflen); return 0; } diff --git a/g10/app-openpgp.c b/g10/app-openpgp.c index c3b4fae11..192680c12 100644 --- a/g10/app-openpgp.c +++ b/g10/app-openpgp.c @@ -1,3803 +1,3804 @@ /* app-openpgp.c - The OpenPGP card application. * Copyright (C) 2003, 2004, 2005, 2007, 2008, * 2009 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 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * $Id$ */ /* Some notes: CHV means Card Holder Verification and is nothing else than a PIN or password. That term seems to have been used originally with GSM cards. Version v2 of the specs changes the term to the clearer term PW for password. We use the terms here interchangeable because we do not want to change existing strings i18n wise. Version 2 of the specs also drops the separate PW2 which was required in v1 due to ISO requirements. It is now possible to have one physical PW but two reference to it so that they can be individually be verified (e.g. to implement a forced verification for one key). Thus you will noticed the use of PW2 with the verify command but not with change_reference_data because the latter operates directly on the physical PW. The Reset Code (RC) as implemented by v2 cards uses the same error counter as the PW2 of v1 cards. By default no RC is set and thus that error counter is set to 0. After setting the RC the error counter will be initialized to 3. */ #include #include #include #include #include #include #include #if GNUPG_MAJOR_VERSION == 1 /* This is used with GnuPG version < 1.9. The code has been source copied from the current GnuPG >= 1.9 and is maintained over there. */ #include "options.h" #include "errors.h" #include "memory.h" #include "util.h" #include "cardglue.h" #else /* GNUPG_MAJOR_VERSION != 1 */ #include "scdaemon.h" #endif /* GNUPG_MAJOR_VERSION != 1 */ #include "i18n.h" #include "iso7816.h" #include "app-common.h" #include "tlv.h" +#include "../include/host2net.h" /* A table describing the DOs of the card. */ static struct { int tag; int constructed; int get_from; /* Constructed DO with this DO or 0 for direct access. */ int binary:1; int dont_cache:1; int flush_on_error:1; int get_immediate_in_v11:1; /* Enable a hack to bypass the cache of this data object if it is used in 1.1 and later versions of the card. This does not work with composite DO and is currently only useful for the CHV status bytes. */ int try_extlen:1; /* Large object; try to use an extended length APDU. */ char *desc; } data_objects[] = { { 0x005E, 0, 0, 1, 0, 0, 0, 0, "Login Data" }, { 0x5F50, 0, 0, 0, 0, 0, 0, 0, "URL" }, { 0x5F52, 0, 0, 1, 0, 0, 0, 0, "Historical Bytes" }, { 0x0065, 1, 0, 1, 0, 0, 0, 0, "Cardholder Related Data"}, { 0x005B, 0, 0x65, 0, 0, 0, 0, 0, "Name" }, { 0x5F2D, 0, 0x65, 0, 0, 0, 0, 0, "Language preferences" }, { 0x5F35, 0, 0x65, 0, 0, 0, 0, 0, "Sex" }, { 0x006E, 1, 0, 1, 0, 0, 0, 0, "Application Related Data" }, { 0x004F, 0, 0x6E, 1, 0, 0, 0, 0, "AID" }, { 0x0073, 1, 0, 1, 0, 0, 0, 0, "Discretionary Data Objects" }, { 0x0047, 0, 0x6E, 1, 1, 0, 0, 0, "Card Capabilities" }, { 0x00C0, 0, 0x6E, 1, 1, 0, 0, 0, "Extended Card Capabilities" }, { 0x00C1, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Signature" }, { 0x00C2, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Decryption" }, { 0x00C3, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Authentication" }, { 0x00C4, 0, 0x6E, 1, 0, 1, 1, 0, "CHV Status Bytes" }, { 0x00C5, 0, 0x6E, 1, 0, 0, 0, 0, "Fingerprints" }, { 0x00C6, 0, 0x6E, 1, 0, 0, 0, 0, "CA Fingerprints" }, { 0x00CD, 0, 0x6E, 1, 0, 0, 0, 0, "Generation time" }, { 0x007A, 1, 0, 1, 0, 0, 0, 0, "Security Support Template" }, { 0x0093, 0, 0x7A, 1, 1, 0, 0, 0, "Digital Signature Counter" }, { 0x0101, 0, 0, 0, 0, 0, 0, 0, "Private DO 1"}, { 0x0102, 0, 0, 0, 0, 0, 0, 0, "Private DO 2"}, { 0x0103, 0, 0, 0, 0, 0, 0, 0, "Private DO 3"}, { 0x0104, 0, 0, 0, 0, 0, 0, 0, "Private DO 4"}, { 0x7F21, 1, 0, 1, 0, 0, 0, 1, "Cardholder certificate"}, { 0 } }; /* The format of RSA private keys. */ typedef enum { RSA_UNKNOWN_FMT, RSA_STD, RSA_STD_N, RSA_CRT, RSA_CRT_N } rsa_key_format_t; /* One cache item for DOs. */ struct cache_s { struct cache_s *next; int tag; size_t length; unsigned char data[1]; }; /* Object with application (i.e. OpenPGP card) specific data. */ struct app_local_s { /* A linked list with cached DOs. */ struct cache_s *cache; /* Keep track of the public keys. */ struct { int read_done; /* True if we have at least tried to read them. */ unsigned char *key; /* This is a malloced buffer with a canonical encoded S-expression encoding a public key. Might be NULL if key is not available. */ size_t keylen; /* The length of the above S-expression. This is usually only required for cross checks because the length of an S-expression is implicitly available. */ } pk[3]; unsigned char status_indicator; /* The card status indicator. */ /* Keep track of the ISO card capabilities. */ struct { unsigned int cmd_chaining:1; /* Command chaining is supported. */ unsigned int ext_lc_le:1; /* Extended Lc and Le are supported. */ } cardcap; /* Keep track of extended card capabilities. */ struct { unsigned int is_v2:1; /* This is a v2.0 compatible card. */ unsigned int get_challenge:1; unsigned int key_import:1; unsigned int change_force_chv:1; unsigned int private_dos:1; unsigned int algo_attr_change:1; /* Algorithm attributes changeable. */ unsigned int sm_supported:1; /* Secure Messaging is supported. */ unsigned int sm_aes128:1; /* Use AES-128 for SM. */ unsigned int max_certlen_3:16; unsigned int max_get_challenge:16; /* Maximum size for get_challenge. */ unsigned int max_cmd_data:16; /* Maximum data size for a command. */ unsigned int max_rsp_data:16; /* Maximum size of a response. */ } extcap; /* Flags used to control the application. */ struct { unsigned int no_sync:1; /* Do not sync CHV1 and CHV2 */ unsigned int def_chv2:1; /* Use 123456 for CHV2. */ } flags; struct { unsigned int n_bits; /* Size of the modulus in bits. The rest of this strucuire is only valid if this is not 0. */ unsigned int e_bits; /* Size of the public exponent in bits. */ rsa_key_format_t format; } keyattr[3]; }; /***** Local prototypes *****/ static unsigned long convert_sig_counter_value (const unsigned char *value, size_t valuelen); static unsigned long get_sig_counter (app_t app); static gpg_error_t do_auth (app_t app, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); static void parse_algorithm_attribute (app_t app, int keyno); static gpg_error_t change_keyattr_from_string (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *value, size_t valuelen); /* Deconstructor. */ static void do_deinit (app_t app) { if (app && app->app_local) { struct cache_s *c, *c2; int i; for (c = app->app_local->cache; c; c = c2) { c2 = c->next; xfree (c); } for (i=0; i < DIM (app->app_local->pk); i++) { xfree (app->app_local->pk[i].key); app->app_local->pk[i].read_done = 0; } xfree (app->app_local); app->app_local = NULL; } } /* Wrapper around iso7816_get_data which first tries to get the data from the cache. With GET_IMMEDIATE passed as true, the cache is bypassed. With TRY_EXTLEN extended lengths APDUs are use if supported by the card. */ static gpg_error_t get_cached_data (app_t app, int tag, unsigned char **result, size_t *resultlen, int get_immediate, int try_extlen) { gpg_error_t err; int i; unsigned char *p; size_t len; struct cache_s *c; int exmode; *result = NULL; *resultlen = 0; if (!get_immediate) { for (c=app->app_local->cache; c; c = c->next) if (c->tag == tag) { if(c->length) { p = xtrymalloc (c->length); if (!p) return gpg_error (gpg_err_code_from_errno (errno)); memcpy (p, c->data, c->length); *result = p; } *resultlen = c->length; return 0; } } if (try_extlen && app->app_local->cardcap.ext_lc_le) exmode = app->app_local->extcap.max_rsp_data; else exmode = 0; err = iso7816_get_data (app->slot, exmode, tag, &p, &len); if (err) return err; *result = p; *resultlen = len; /* Check whether we should cache this object. */ if (get_immediate) return 0; for (i=0; data_objects[i].tag; i++) if (data_objects[i].tag == tag) { if (data_objects[i].dont_cache) return 0; break; } /* Okay, cache it. */ for (c=app->app_local->cache; c; c = c->next) assert (c->tag != tag); c = xtrymalloc (sizeof *c + len); if (c) { memcpy (c->data, p, len); c->length = len; c->tag = tag; c->next = app->app_local->cache; app->app_local->cache = c; } return 0; } /* Remove DO at TAG from the cache. */ static void flush_cache_item (app_t app, int tag) { struct cache_s *c, *cprev; int i; if (!app->app_local) return; for (c=app->app_local->cache, cprev=NULL; c ; cprev=c, c = c->next) if (c->tag == tag) { if (cprev) cprev->next = c->next; else app->app_local->cache = c->next; xfree (c); for (c=app->app_local->cache; c ; c = c->next) { assert (c->tag != tag); /* Oops: duplicated entry. */ } return; } /* Try again if we have an outer tag. */ for (i=0; data_objects[i].tag; i++) if (data_objects[i].tag == tag && data_objects[i].get_from && data_objects[i].get_from != tag) flush_cache_item (app, data_objects[i].get_from); } /* Flush all entries from the cache which might be out of sync after an error. */ static void flush_cache_after_error (app_t app) { int i; for (i=0; data_objects[i].tag; i++) if (data_objects[i].flush_on_error) flush_cache_item (app, data_objects[i].tag); } /* Flush the entire cache. */ static void flush_cache (app_t app) { if (app && app->app_local) { struct cache_s *c, *c2; for (c = app->app_local->cache; c; c = c2) { c2 = c->next; xfree (c); } app->app_local->cache = NULL; } } /* Get the DO identified by TAG from the card in SLOT and return a buffer with its content in RESULT and NBYTES. The return value is NULL if not found or a pointer which must be used to release the buffer holding value. */ static void * get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes, int *r_rc) { int rc, i; unsigned char *buffer; size_t buflen; unsigned char *value; size_t valuelen; int dummyrc; int exmode; if (!r_rc) r_rc = &dummyrc; *result = NULL; *nbytes = 0; *r_rc = 0; for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++) ; if (app->card_version > 0x0100 && data_objects[i].get_immediate_in_v11) { if (data_objects[i].try_extlen && app->app_local->cardcap.ext_lc_le) exmode = app->app_local->extcap.max_rsp_data; else exmode = 0; rc = iso7816_get_data (app->slot, exmode, tag, &buffer, &buflen); if (rc) { *r_rc = rc; return NULL; } *result = buffer; *nbytes = buflen; return buffer; } value = NULL; rc = -1; if (data_objects[i].tag && data_objects[i].get_from) { rc = get_cached_data (app, data_objects[i].get_from, &buffer, &buflen, (data_objects[i].dont_cache || data_objects[i].get_immediate_in_v11), data_objects[i].try_extlen); if (!rc) { const unsigned char *s; s = find_tlv_unchecked (buffer, buflen, tag, &valuelen); if (!s) value = NULL; /* not found */ else if (valuelen > buflen - (s - buffer)) { log_error ("warning: constructed DO too short\n"); value = NULL; xfree (buffer); buffer = NULL; } else value = buffer + (s - buffer); } } if (!value) /* Not in a constructed DO, try simple. */ { rc = get_cached_data (app, tag, &buffer, &buflen, (data_objects[i].dont_cache || data_objects[i].get_immediate_in_v11), data_objects[i].try_extlen); if (!rc) { value = buffer; valuelen = buflen; } } if (!rc) { *nbytes = valuelen; *result = value; return buffer; } *r_rc = rc; return NULL; } static void dump_all_do (int slot) { int rc, i, j; unsigned char *buffer; size_t buflen; for (i=0; data_objects[i].tag; i++) { if (data_objects[i].get_from) continue; /* We don't try extended length APDU because such large DO would be pretty useless in a log file. */ rc = iso7816_get_data (slot, 0, data_objects[i].tag, &buffer, &buflen); if (gpg_err_code (rc) == GPG_ERR_NO_OBJ) ; else if (rc) log_info ("DO `%s' not available: %s\n", data_objects[i].desc, gpg_strerror (rc)); else { if (data_objects[i].binary) { log_info ("DO `%s': ", data_objects[i].desc); log_printhex ("", buffer, buflen); } else log_info ("DO `%s': `%.*s'\n", data_objects[i].desc, (int)buflen, buffer); /* FIXME: sanitize */ if (data_objects[i].constructed) { for (j=0; data_objects[j].tag; j++) { const unsigned char *value; size_t valuelen; if (j==i || data_objects[i].tag != data_objects[j].get_from) continue; value = find_tlv_unchecked (buffer, buflen, data_objects[j].tag, &valuelen); if (!value) ; /* not found */ else if (valuelen > buflen - (value - buffer)) log_error ("warning: constructed DO too short\n"); else { if (data_objects[j].binary) { log_info ("DO `%s': ", data_objects[j].desc); if (valuelen > 200) log_info ("[%u]\n", (unsigned int)valuelen); else log_printhex ("", value, valuelen); } else log_info ("DO `%s': `%.*s'\n", data_objects[j].desc, (int)valuelen, value); /* FIXME: sanitize */ } } } } xfree (buffer); buffer = NULL; } } /* Count the number of bits, assuming the A represents an unsigned big integer of length LEN bytes. */ static unsigned int count_bits (const unsigned char *a, size_t len) { unsigned int n = len * 8; int i; for (; len && !*a; len--, a++, n -=8) ; if (len) { for (i=7; i && !(*a & (1< Were FLAGS is a plain hexadecimal number representing flag values. The lsb is here the rightmost bit. Defined flags bits are: Bit 0 = CHV1 and CHV2 are not syncronized Bit 1 = CHV2 has been been set to the default PIN of "123456" (this implies that bit 0 is also set). */ static void parse_login_data (app_t app) { unsigned char *buffer, *p; size_t buflen, len; void *relptr; /* Set defaults. */ app->app_local->flags.no_sync = 0; app->app_local->flags.def_chv2 = 0; /* Read the DO. */ relptr = get_one_do (app, 0x005E, &buffer, &buflen, NULL); if (!relptr) return; /* Ooops. */ for (; buflen; buflen--, buffer++) if (*buffer == '\n') break; if (buflen < 2 || buffer[1] != '\x14') return; /* No control sequences. */ buflen--; buffer++; do { buflen--; buffer++; if (buflen > 1 && *buffer == 'F' && buffer[1] == '=') { /* Flags control sequence found. */ int lastdig = 0; /* For now we are only interested in the last digit, so skip any leading digits but bail out on invalid characters. */ for (p=buffer+2, len = buflen-2; len && hexdigitp (p); p++, len--) lastdig = xtoi_1 (p); if (len && !(*p == '\n' || *p == '\x18')) goto next; /* Invalid characters in field. */ app->app_local->flags.no_sync = !!(lastdig & 1); app->app_local->flags.def_chv2 = (lastdig & 3) == 3; } next: for (; buflen && *buffer != '\x18'; buflen--, buffer++) if (*buffer == '\n') buflen = 1; } while (buflen); xfree (relptr); } /* Note, that FPR must be at least 20 bytes. */ static gpg_error_t store_fpr (app_t app, int keynumber, u32 timestamp, const unsigned char *m, size_t mlen, const unsigned char *e, size_t elen, unsigned char *fpr, unsigned int card_version) { unsigned int n, nbits; unsigned char *buffer, *p; int tag, tag2; int rc; for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */ ; for (; elen && !*e; elen--, e++) /* strip leading zeroes */ ; n = 6 + 2 + mlen + 2 + elen; p = buffer = xtrymalloc (3 + n); if (!buffer) return gpg_error_from_syserror (); *p++ = 0x99; /* ctb */ *p++ = n >> 8; /* 2 byte length header */ *p++ = n; *p++ = 4; /* key packet version */ *p++ = timestamp >> 24; *p++ = timestamp >> 16; *p++ = timestamp >> 8; *p++ = timestamp; *p++ = 1; /* RSA */ nbits = count_bits (m, mlen); *p++ = nbits >> 8; *p++ = nbits; memcpy (p, m, mlen); p += mlen; nbits = count_bits (e, elen); *p++ = nbits >> 8; *p++ = nbits; memcpy (p, e, elen); p += elen; gcry_md_hash_buffer (GCRY_MD_SHA1, fpr, buffer, n+3); xfree (buffer); tag = (card_version > 0x0007? 0xC7 : 0xC6) + keynumber; flush_cache_item (app, 0xC5); tag2 = 0xCE + keynumber; flush_cache_item (app, 0xCD); rc = iso7816_put_data (app->slot, 0, tag, fpr, 20); if (rc) log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc)); if (!rc && card_version > 0x0100) { unsigned char buf[4]; buf[0] = timestamp >> 24; buf[1] = timestamp >> 16; buf[2] = timestamp >> 8; buf[3] = timestamp; rc = iso7816_put_data (app->slot, 0, tag2, buf, 4); if (rc) log_error (_("failed to store the creation date: %s\n"), gpg_strerror (rc)); } return rc; } static void send_fpr_if_not_null (ctrl_t ctrl, const char *keyword, int number, const unsigned char *fpr) { int i; char buf[41]; char numbuf[25]; for (i=0; i < 20 && !fpr[i]; i++) ; if (i==20) return; /* All zero. */ bin2hex (fpr, 20, buf); if (number == -1) *numbuf = 0; /* Don't print the key number */ else sprintf (numbuf, "%d", number); send_status_info (ctrl, keyword, numbuf, (size_t)strlen(numbuf), buf, (size_t)strlen (buf), NULL, 0); } static void send_fprtime_if_not_null (ctrl_t ctrl, const char *keyword, int number, const unsigned char *stamp) { char numbuf1[50], numbuf2[50]; unsigned long value; - value = (stamp[0] << 24) | (stamp[1]<<16) | (stamp[2]<<8) | stamp[3]; + value = buf32_to_ulong (stamp); if (!value) return; sprintf (numbuf1, "%d", number); sprintf (numbuf2, "%lu", value); send_status_info (ctrl, keyword, numbuf1, (size_t)strlen(numbuf1), numbuf2, (size_t)strlen(numbuf2), NULL, 0); } static void send_key_data (ctrl_t ctrl, const char *name, const unsigned char *a, size_t alen) { char *buf; buf = bin2hex (a, alen, NULL); if (!buf) { log_error ("memory allocation error in send_key_data\n"); return; } send_status_info (ctrl, "KEY-DATA", name, (size_t)strlen(name), buf, (size_t)strlen (buf), NULL, 0); xfree (buf); } static void send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int number) { char buffer[200]; assert (number >=0 && number < DIM(app->app_local->keyattr)); /* We only support RSA thus the algo identifier is fixed to 1. */ snprintf (buffer, sizeof buffer, "%d 1 %u %u %d", number+1, app->app_local->keyattr[number].n_bits, app->app_local->keyattr[number].e_bits, app->app_local->keyattr[number].format); send_status_direct (ctrl, keyword, buffer); } /* Implement the GETATTR command. This is similar to the LEARN command but returns just one value via the status interface. */ static gpg_error_t do_getattr (app_t app, ctrl_t ctrl, const char *name) { static struct { const char *name; int tag; int special; } table[] = { { "DISP-NAME", 0x005B }, { "LOGIN-DATA", 0x005E }, { "DISP-LANG", 0x5F2D }, { "DISP-SEX", 0x5F35 }, { "PUBKEY-URL", 0x5F50 }, { "KEY-FPR", 0x00C5, 3 }, { "KEY-TIME", 0x00CD, 4 }, { "KEY-ATTR", 0x0000, -5 }, { "CA-FPR", 0x00C6, 3 }, { "CHV-STATUS", 0x00C4, 1 }, { "SIG-COUNTER", 0x0093, 2 }, { "SERIALNO", 0x004F, -1 }, { "AID", 0x004F }, { "EXTCAP", 0x0000, -2 }, { "PRIVATE-DO-1", 0x0101 }, { "PRIVATE-DO-2", 0x0102 }, { "PRIVATE-DO-3", 0x0103 }, { "PRIVATE-DO-4", 0x0104 }, { "$AUTHKEYID", 0x0000, -3 }, { "$DISPSERIALNO",0x0000, -4 }, { NULL, 0 } }; int idx, i, rc; void *relptr; unsigned char *value; size_t valuelen; for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++) ; if (!table[idx].name) return gpg_error (GPG_ERR_INV_NAME); if (table[idx].special == -1) { /* The serial number is very special. We could have used the AID DO to retrieve it, but we have it already in the app context and the stamp argument is required anyway which we can't by other means. The AID DO is available anyway but not hex formatted. */ char *serial; time_t stamp; char tmp[50]; if (!app_get_serial_and_stamp (app, &serial, &stamp)) { sprintf (tmp, "%lu", (unsigned long)stamp); send_status_info (ctrl, "SERIALNO", serial, strlen (serial), tmp, strlen (tmp), NULL, 0); xfree (serial); } return 0; } if (table[idx].special == -2) { char tmp[100]; snprintf (tmp, sizeof tmp, "gc=%d ki=%d fc=%d pd=%d mcl3=%u aac=%d sm=%d", app->app_local->extcap.get_challenge, app->app_local->extcap.key_import, app->app_local->extcap.change_force_chv, app->app_local->extcap.private_dos, app->app_local->extcap.max_certlen_3, app->app_local->extcap.algo_attr_change, (app->app_local->extcap.sm_supported ? (app->app_local->extcap.sm_aes128? 7 : 2) : 0)); send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); return 0; } if (table[idx].special == -3) { char const tmp[] = "OPENPGP.3"; send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); return 0; } if (table[idx].special == -4) { char *serial; time_t stamp; if (!app_get_serial_and_stamp (app, &serial, &stamp)) { if (strlen (serial) > 16+12) { send_status_info (ctrl, table[idx].name, serial+16, 12, NULL, 0); xfree (serial); return 0; } xfree (serial); } return gpg_error (GPG_ERR_INV_NAME); } if (table[idx].special == -5) { for (i=0; i < 3; i++) send_key_attr (ctrl, app, table[idx].name, i); return 0; } relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &rc); if (relptr) { if (table[idx].special == 1) { char numbuf[7*23]; for (i=0,*numbuf=0; i < valuelen && i < 7; i++) sprintf (numbuf+strlen (numbuf), " %d", value[i]); send_status_info (ctrl, table[idx].name, numbuf, strlen (numbuf), NULL, 0); } else if (table[idx].special == 2) { char numbuf[50]; sprintf (numbuf, "%lu", convert_sig_counter_value (value, valuelen)); send_status_info (ctrl, table[idx].name, numbuf, strlen (numbuf), NULL, 0); } else if (table[idx].special == 3) { if (valuelen >= 60) for (i=0; i < 3; i++) send_fpr_if_not_null (ctrl, table[idx].name, i+1, value+i*20); } else if (table[idx].special == 4) { if (valuelen >= 12) for (i=0; i < 3; i++) send_fprtime_if_not_null (ctrl, table[idx].name, i+1, value+i*4); } else send_status_info (ctrl, table[idx].name, value, valuelen, NULL, 0); xfree (relptr); } return rc; } /* Retrieve the fingerprint from the card inserted in SLOT and write the according hex representation to FPR. Caller must have provide a buffer at FPR of least 41 bytes. Returns 0 on success or an error code. */ #if GNUPG_MAJOR_VERSION > 1 static gpg_error_t retrieve_fpr_from_card (app_t app, int keyno, char *fpr) { gpg_error_t err = 0; void *relptr; unsigned char *value; size_t valuelen; assert (keyno >=0 && keyno <= 2); relptr = get_one_do (app, 0x00C5, &value, &valuelen, NULL); if (relptr && valuelen >= 60) bin2hex (value+keyno*20, 20, fpr); else err = gpg_error (GPG_ERR_NOT_FOUND); xfree (relptr); return err; } #endif /*GNUPG_MAJOR_VERSION > 1*/ /* Retrieve the public key material for the RSA key, whose fingerprint is FPR, from gpg output, which can be read through the stream FP. The RSA modulus will be stored at the address of M and MLEN, the public exponent at E and ELEN. Returns zero on success, an error code on failure. Caller must release the allocated buffers at M and E if the function returns success. */ #if GNUPG_MAJOR_VERSION > 1 static gpg_error_t retrieve_key_material (FILE *fp, const char *hexkeyid, const unsigned char **m, size_t *mlen, const unsigned char **e, size_t *elen) { gcry_error_t err = 0; char *line = NULL; /* read_line() buffer. */ size_t line_size = 0; /* Helper for for read_line. */ int found_key = 0; /* Helper to find a matching key. */ unsigned char *m_new = NULL; unsigned char *e_new = NULL; size_t m_new_n = 0; size_t e_new_n = 0; /* Loop over all records until we have found the subkey corresponding to the fingerprint. Inm general the first record should be the pub record, but we don't rely on that. Given that we only need to look at one key, it is sufficient to compare the keyid so that we don't need to look at "fpr" records. */ for (;;) { char *p; char *fields[6]; int nfields; size_t max_length; gcry_mpi_t mpi; int i; max_length = 4096; i = read_line (fp, &line, &line_size, &max_length); if (!i) break; /* EOF. */ if (i < 0) { err = gpg_error_from_syserror (); goto leave; /* Error. */ } if (!max_length) { err = gpg_error (GPG_ERR_TRUNCATED); goto leave; /* Line truncated - we better stop processing. */ } /* Parse the line into fields. */ for (nfields=0, p=line; p && nfields < DIM (fields); nfields++) { fields[nfields] = p; p = strchr (p, ':'); if (p) *(p++) = 0; } if (!nfields) continue; /* No fields at all - skip line. */ if (!found_key) { if ( (!strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") ) && nfields > 4 && !strcmp (fields[4], hexkeyid)) found_key = 1; continue; } if ( !strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") ) break; /* Next key - stop. */ if ( strcmp (fields[0], "pkd") ) continue; /* Not a key data record. */ i = 0; /* Avoid erroneous compiler warning. */ if ( nfields < 4 || (i = atoi (fields[1])) < 0 || i > 1 || (!i && m_new) || (i && e_new)) { err = gpg_error (GPG_ERR_GENERAL); goto leave; /* Error: Invalid key data record or not an RSA key. */ } err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_HEX, fields[3], 0, NULL); if (err) mpi = NULL; else if (!i) err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &m_new, &m_new_n, mpi); else err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &e_new, &e_new_n, mpi); gcry_mpi_release (mpi); if (err) goto leave; } if (m_new && e_new) { *m = m_new; *mlen = m_new_n; m_new = NULL; *e = e_new; *elen = e_new_n; e_new = NULL; } else err = gpg_error (GPG_ERR_GENERAL); leave: xfree (m_new); xfree (e_new); xfree (line); return err; } #endif /*GNUPG_MAJOR_VERSION > 1*/ /* Get the public key for KEYNO and store it as an S-expresion with the APP handle. On error that field gets cleared. If we already know about the public key we will just return. Note that this does not mean a key is available; this is soley indicated by the presence of the app->app_local->pk[KEYNO-1].key field. Note that GnuPG 1.x does not need this and it would be too time consuming to send it just for the fun of it. However, given that we use the same code in gpg 1.4, we can't use the gcry S-expresion here but need to open encode it. */ #if GNUPG_MAJOR_VERSION > 1 static gpg_error_t get_public_key (app_t app, int keyno) { gpg_error_t err = 0; unsigned char *buffer; const unsigned char *keydata, *m, *e; size_t buflen, keydatalen, mlen, elen; unsigned char *mbuf = NULL; unsigned char *ebuf = NULL; char *keybuf = NULL; char *keybuf_p; if (keyno < 1 || keyno > 3) return gpg_error (GPG_ERR_INV_ID); keyno--; /* Already cached? */ if (app->app_local->pk[keyno].read_done) return 0; xfree (app->app_local->pk[keyno].key); app->app_local->pk[keyno].key = NULL; app->app_local->pk[keyno].keylen = 0; m = e = NULL; /* (avoid cc warning) */ if (app->card_version > 0x0100) { int exmode, le_value; /* We may simply read the public key out of these cards. */ if (app->app_local->cardcap.ext_lc_le) { exmode = 1; /* Use extended length. */ le_value = app->app_local->extcap.max_rsp_data; } else { exmode = 0; le_value = 256; /* Use legacy value. */ } err = iso7816_read_public_key (app->slot, exmode, (const unsigned char*)(keyno == 0? "\xB6" : keyno == 1? "\xB8" : "\xA4"), 2, le_value, &buffer, &buflen); if (err) { log_error (_("reading public key failed: %s\n"), gpg_strerror (err)); goto leave; } keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen); if (!keydata) { err = gpg_error (GPG_ERR_CARD); log_error (_("response does not contain the public key data\n")); goto leave; } m = find_tlv (keydata, keydatalen, 0x0081, &mlen); if (!m) { err = gpg_error (GPG_ERR_CARD); log_error (_("response does not contain the RSA modulus\n")); goto leave; } e = find_tlv (keydata, keydatalen, 0x0082, &elen); if (!e) { err = gpg_error (GPG_ERR_CARD); log_error (_("response does not contain the RSA public exponent\n")); goto leave; } /* Prepend numbers with a 0 if needed. */ if (mlen && (*m & 0x80)) { mbuf = xtrymalloc ( mlen + 1); if (!mbuf) { err = gpg_error_from_syserror (); goto leave; } *mbuf = 0; memcpy (mbuf+1, m, mlen); mlen++; m = mbuf; } if (elen && (*e & 0x80)) { ebuf = xtrymalloc ( elen + 1); if (!ebuf) { err = gpg_error_from_syserror (); goto leave; } *ebuf = 0; memcpy (ebuf+1, e, elen); elen++; e = ebuf; } } else { /* Due to a design problem in v1.0 cards we can't get the public key out of these cards without doing a verify on CHV3. Clearly that is not an option and thus we try to locate the key using an external helper. The helper we use here is gpg itself, which should know about the key in any case. */ char fpr[41]; char *hexkeyid; char *command = NULL; FILE *fp; int ret; buffer = NULL; /* We don't need buffer. */ err = retrieve_fpr_from_card (app, keyno, fpr); if (err) { log_error ("error while retrieving fpr from card: %s\n", gpg_strerror (err)); goto leave; } hexkeyid = fpr + 24; ret = estream_asprintf (&command, "gpg --list-keys --with-colons --with-key-data '%s'", fpr); if (ret < 0) { err = gpg_error_from_syserror (); goto leave; } fp = popen (command, "r"); xfree (command); if (!fp) { err = gpg_error_from_syserror (); log_error ("running gpg failed: %s\n", gpg_strerror (err)); goto leave; } err = retrieve_key_material (fp, hexkeyid, &m, &mlen, &e, &elen); pclose (fp); if (err) { log_error ("error while retrieving key material through pipe: %s\n", gpg_strerror (err)); goto leave; } } /* Allocate a buffer to construct the S-expression. */ /* FIXME: We should provide a generalized S-expression creation mechanism. */ keybuf = xtrymalloc (50 + 2*35 + mlen + elen + 1); if (!keybuf) { err = gpg_error_from_syserror (); goto leave; } sprintf (keybuf, "(10:public-key(3:rsa(1:n%u:", (unsigned int) mlen); keybuf_p = keybuf + strlen (keybuf); memcpy (keybuf_p, m, mlen); keybuf_p += mlen; sprintf (keybuf_p, ")(1:e%u:", (unsigned int)elen); keybuf_p += strlen (keybuf_p); memcpy (keybuf_p, e, elen); keybuf_p += elen; strcpy (keybuf_p, ")))"); keybuf_p += strlen (keybuf_p); app->app_local->pk[keyno].key = (unsigned char*)keybuf; app->app_local->pk[keyno].keylen = (keybuf_p - keybuf); leave: /* Set a flag to indicate that we tried to read the key. */ app->app_local->pk[keyno].read_done = 1; xfree (buffer); xfree (mbuf); xfree (ebuf); return 0; } #endif /* GNUPG_MAJOR_VERSION > 1 */ /* Send the KEYPAIRINFO back. KEYNO needs to be in the range [1,3]. This is used by the LEARN command. */ static gpg_error_t send_keypair_info (app_t app, ctrl_t ctrl, int keyno) { gpg_error_t err = 0; /* Note that GnuPG 1.x does not need this and it would be too time consuming to send it just for the fun of it. */ #if GNUPG_MAJOR_VERSION > 1 unsigned char grip[20]; char gripstr[41]; char idbuf[50]; err = get_public_key (app, keyno); if (err) goto leave; assert (keyno >= 1 && keyno <= 3); if (!app->app_local->pk[keyno-1].key) goto leave; /* No such key - ignore. */ err = keygrip_from_canon_sexp (app->app_local->pk[keyno-1].key, app->app_local->pk[keyno-1].keylen, grip); if (err) goto leave; bin2hex (grip, 20, gripstr); sprintf (idbuf, "OPENPGP.%d", keyno); send_status_info (ctrl, "KEYPAIRINFO", gripstr, 40, idbuf, strlen (idbuf), NULL, (size_t)0); leave: #endif /* GNUPG_MAJOR_VERSION > 1 */ return err; } /* Handle the LEARN command for OpenPGP. */ static gpg_error_t do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) { (void)flags; do_getattr (app, ctrl, "EXTCAP"); do_getattr (app, ctrl, "DISP-NAME"); do_getattr (app, ctrl, "DISP-LANG"); do_getattr (app, ctrl, "DISP-SEX"); do_getattr (app, ctrl, "PUBKEY-URL"); do_getattr (app, ctrl, "LOGIN-DATA"); do_getattr (app, ctrl, "KEY-FPR"); if (app->card_version > 0x0100) do_getattr (app, ctrl, "KEY-TIME"); do_getattr (app, ctrl, "CA-FPR"); do_getattr (app, ctrl, "CHV-STATUS"); do_getattr (app, ctrl, "SIG-COUNTER"); if (app->app_local->extcap.private_dos) { do_getattr (app, ctrl, "PRIVATE-DO-1"); do_getattr (app, ctrl, "PRIVATE-DO-2"); if (app->did_chv2) do_getattr (app, ctrl, "PRIVATE-DO-3"); if (app->did_chv3) do_getattr (app, ctrl, "PRIVATE-DO-4"); } send_keypair_info (app, ctrl, 1); send_keypair_info (app, ctrl, 2); send_keypair_info (app, ctrl, 3); /* Note: We do not send the Cardholder Certificate, because that is relativly long and for OpenPGP applications not really needed. */ return 0; } /* Handle the READKEY command for OpenPGP. On success a canonical encoded S-expression with the public key will get stored at PK and its length (for assertions) at PKLEN; the caller must release that buffer. On error PK and PKLEN are not changed and an error code is returned. */ static gpg_error_t do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) { #if GNUPG_MAJOR_VERSION > 1 gpg_error_t err; int keyno; unsigned char *buf; if (!strcmp (keyid, "OPENPGP.1")) keyno = 1; else if (!strcmp (keyid, "OPENPGP.2")) keyno = 2; else if (!strcmp (keyid, "OPENPGP.3")) keyno = 3; else return gpg_error (GPG_ERR_INV_ID); err = get_public_key (app, keyno); if (err) return err; buf = app->app_local->pk[keyno-1].key; if (!buf) return gpg_error (GPG_ERR_NO_PUBKEY); *pklen = app->app_local->pk[keyno-1].keylen;; *pk = xtrymalloc (*pklen); if (!*pk) { err = gpg_error_from_syserror (); *pklen = 0; return err; } memcpy (*pk, buf, *pklen); return 0; #else return gpg_error (GPG_ERR_NOT_IMPLEMENTED); #endif } /* Read the standard certificate of an OpenPGP v2 card. It is returned in a freshly allocated buffer with that address stored at CERT and the length of the certificate stored at CERTLEN. CERTID needs to be set to "OPENPGP.3". */ static gpg_error_t do_readcert (app_t app, const char *certid, unsigned char **cert, size_t *certlen) { #if GNUPG_MAJOR_VERSION > 1 gpg_error_t err; unsigned char *buffer; size_t buflen; void *relptr; *cert = NULL; *certlen = 0; if (strcmp (certid, "OPENPGP.3")) return gpg_error (GPG_ERR_INV_ID); if (!app->app_local->extcap.is_v2) return gpg_error (GPG_ERR_NOT_FOUND); relptr = get_one_do (app, 0x7F21, &buffer, &buflen, NULL); if (!relptr) return gpg_error (GPG_ERR_NOT_FOUND); if (!buflen) err = gpg_error (GPG_ERR_NOT_FOUND); else if (!(*cert = xtrymalloc (buflen))) err = gpg_error_from_syserror (); else { memcpy (*cert, buffer, buflen); *certlen = buflen; err = 0; } xfree (relptr); return err; #else return gpg_error (GPG_ERR_NOT_IMPLEMENTED); #endif } /* Verify a CHV either using using the pinentry or if possibile by using a keypad. PINCB and PINCB_ARG describe the usual callback for the pinentry. CHVNO must be either 1 or 2. SIGCOUNT is only used with CHV1. PINVALUE is the address of a pointer which will receive a newly allocated block with the actual PIN (this is useful in case that PIN shall be used for another verify operation). The caller needs to free this value. If the function returns with success and NULL is stored at PINVALUE, the caller should take this as an indication that the keypad has been used. */ static gpg_error_t verify_a_chv (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, int chvno, unsigned long sigcount, char **pinvalue) { int rc = 0; char *prompt_buffer = NULL; const char *prompt; iso7816_pininfo_t pininfo; int minlen = 6; assert (chvno == 1 || chvno == 2); *pinvalue = NULL; if (chvno == 2 && app->app_local->flags.def_chv2) { /* Special case for def_chv2 mechanism. */ if (opt.verbose) log_info (_("using default PIN as %s\n"), "CHV2"); rc = iso7816_verify (app->slot, 0x82, "123456", 6); if (rc) { /* Verification of CHV2 with the default PIN failed, although the card pretends to have the default PIN set as CHV2. We better disable the def_chv2 flag now. */ log_info (_("failed to use default PIN as %s: %s" " - disabling further default use\n"), "CHV2", gpg_strerror (rc)); app->app_local->flags.def_chv2 = 0; } return rc; } memset (&pininfo, 0, sizeof pininfo); pininfo.mode = 1; pininfo.minlen = minlen; if (chvno == 1) { #define PROMPTSTRING _("||Please enter the PIN%%0A[sigs done: %lu]") size_t promptsize = strlen (PROMPTSTRING) + 50; prompt_buffer = xtrymalloc (promptsize); if (!prompt_buffer) return gpg_error_from_syserror (); snprintf (prompt_buffer, promptsize-1, PROMPTSTRING, sigcount); prompt = prompt_buffer; #undef PROMPTSTRING } else prompt = _("||Please enter the PIN"); if (!opt.disable_keypad && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) ) { /* The reader supports the verify command through the keypad. Note that the pincb appends a text to the prompt telling the user to use the keypad. */ rc = pincb (pincb_arg, prompt, NULL); prompt = NULL; xfree (prompt_buffer); prompt_buffer = NULL; if (rc) { log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc)); return rc; } rc = iso7816_verify_kp (app->slot, 0x80+chvno, "", 0, &pininfo); /* Dismiss the prompt. */ pincb (pincb_arg, NULL, NULL); assert (!*pinvalue); } else { /* The reader has no keypad or we don't want to use it. */ rc = pincb (pincb_arg, prompt, pinvalue); prompt = NULL; xfree (prompt_buffer); prompt_buffer = NULL; if (rc) { log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc)); return rc; } if (strlen (*pinvalue) < minlen) { log_error (_("PIN for CHV%d is too short;" " minimum length is %d\n"), chvno, minlen); xfree (*pinvalue); *pinvalue = NULL; return gpg_error (GPG_ERR_BAD_PIN); } rc = iso7816_verify (app->slot, 0x80+chvno, *pinvalue, strlen (*pinvalue)); } if (rc) { log_error (_("verify CHV%d failed: %s\n"), chvno, gpg_strerror (rc)); xfree (*pinvalue); *pinvalue = NULL; flush_cache_after_error (app); } return rc; } /* Verify CHV2 if required. Depending on the configuration of the card CHV1 will also be verified. */ static gpg_error_t verify_chv2 (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int rc; char *pinvalue; if (app->did_chv2) return 0; /* We already verified CHV2. */ rc = verify_a_chv (app, pincb, pincb_arg, 2, 0, &pinvalue); if (rc) return rc; app->did_chv2 = 1; if (!app->did_chv1 && !app->force_chv1 && pinvalue) { /* For convenience we verify CHV1 here too. We do this only if the card is not configured to require a verification before each CHV1 controlled operation (force_chv1) and if we are not using the keypad (PINVALUE == NULL). */ rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue)); if (gpg_err_code (rc) == GPG_ERR_BAD_PIN) rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED); if (rc) { log_error (_("verify CHV%d failed: %s\n"), 1, gpg_strerror (rc)); flush_cache_after_error (app); } else app->did_chv1 = 1; } xfree (pinvalue); return rc; } /* Build the prompt to enter the Admin PIN. The prompt depends on the current sdtate of the card. */ static gpg_error_t build_enter_admin_pin_prompt (app_t app, char **r_prompt) { void *relptr; unsigned char *value; size_t valuelen; int remaining; char *prompt; *r_prompt = NULL; relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL); if (!relptr || valuelen < 7) { log_error (_("error retrieving CHV status from card\n")); xfree (relptr); return gpg_error (GPG_ERR_CARD); } if (value[6] == 0) { log_info (_("card is permanently locked!\n")); xfree (relptr); return gpg_error (GPG_ERR_BAD_PIN); } remaining = value[6]; xfree (relptr); log_info(_("%d Admin PIN attempts remaining before card" " is permanently locked\n"), remaining); if (remaining < 3) { /* TRANSLATORS: Do not translate the "|A|" prefix but keep it at the start of the string. Use %%0A to force a linefeed. */ prompt = xtryasprintf (_("|A|Please enter the Admin PIN%%0A" "[remaining attempts: %d]"), remaining); } else prompt = xtrystrdup (_("|A|Please enter the Admin PIN")); if (!prompt) return gpg_error_from_syserror (); *r_prompt = prompt; return 0; } /* Verify CHV3 if required. */ static gpg_error_t verify_chv3 (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int rc = 0; #if GNUPG_MAJOR_VERSION != 1 if (!opt.allow_admin) { log_info (_("access to admin commands is not configured\n")); return gpg_error (GPG_ERR_EACCES); } #endif if (!app->did_chv3) { iso7816_pininfo_t pininfo; int minlen = 8; char *prompt; memset (&pininfo, 0, sizeof pininfo); pininfo.mode = 1; pininfo.minlen = minlen; rc = build_enter_admin_pin_prompt (app, &prompt); if (rc) return rc; if (!opt.disable_keypad && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) ) { /* The reader supports the verify command through the keypad. */ rc = pincb (pincb_arg, prompt, NULL); xfree (prompt); prompt = NULL; if (rc) { log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc)); return rc; } rc = iso7816_verify_kp (app->slot, 0x83, "", 0, &pininfo); /* Dismiss the prompt. */ pincb (pincb_arg, NULL, NULL); } else { char *pinvalue; rc = pincb (pincb_arg, prompt, &pinvalue); xfree (prompt); prompt = NULL; if (rc) { log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc)); return rc; } if (strlen (pinvalue) < minlen) { log_error (_("PIN for CHV%d is too short;" " minimum length is %d\n"), 3, minlen); xfree (pinvalue); return gpg_error (GPG_ERR_BAD_PIN); } rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue)); xfree (pinvalue); } if (rc) { log_error (_("verify CHV%d failed: %s\n"), 3, gpg_strerror (rc)); flush_cache_after_error (app); return rc; } app->did_chv3 = 1; } return rc; } /* Handle the SETATTR operation. All arguments are already basically checked. */ static gpg_error_t do_setattr (app_t app, const char *name, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *value, size_t valuelen) { gpg_error_t rc; int idx; static struct { const char *name; int tag; int need_chv; int special; unsigned int need_v2:1; } table[] = { { "DISP-NAME", 0x005B, 3 }, { "LOGIN-DATA", 0x005E, 3, 2 }, { "DISP-LANG", 0x5F2D, 3 }, { "DISP-SEX", 0x5F35, 3 }, { "PUBKEY-URL", 0x5F50, 3 }, { "CHV-STATUS-1", 0x00C4, 3, 1 }, { "CA-FPR-1", 0x00CA, 3 }, { "CA-FPR-2", 0x00CB, 3 }, { "CA-FPR-3", 0x00CC, 3 }, { "PRIVATE-DO-1", 0x0101, 2 }, { "PRIVATE-DO-2", 0x0102, 3 }, { "PRIVATE-DO-3", 0x0103, 2 }, { "PRIVATE-DO-4", 0x0104, 3 }, { "CERT-3", 0x7F21, 3, 0, 1 }, { "SM-KEY-ENC", 0x00D1, 3, 0, 1 }, { "SM-KEY-MAC", 0x00D2, 3, 0, 1 }, { "KEY-ATTR", 0, 0, 3, 1 }, { NULL, 0 } }; int exmode; for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++) ; if (!table[idx].name) return gpg_error (GPG_ERR_INV_NAME); if (table[idx].need_v2 && !app->app_local->extcap.is_v2) return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Not yet supported. */ if (table[idx].special == 3) return change_keyattr_from_string (app, pincb, pincb_arg, value, valuelen); switch (table[idx].need_chv) { case 2: rc = verify_chv2 (app, pincb, pincb_arg); break; case 3: rc = verify_chv3 (app, pincb, pincb_arg); break; default: rc = 0; } if (rc) return rc; /* Flush the cache before writing it, so that the next get operation will reread the data from the card and thus get synced in case of errors (e.g. data truncated by the card). */ flush_cache_item (app, table[idx].tag); if (app->app_local->cardcap.ext_lc_le && valuelen > 254) exmode = 1; /* Use extended length w/o a limit. */ else if (app->app_local->cardcap.cmd_chaining && valuelen > 254) exmode = -254; /* Command chaining with max. 254 bytes. */ else exmode = 0; rc = iso7816_put_data (app->slot, exmode, table[idx].tag, value, valuelen); if (rc) log_error ("failed to set `%s': %s\n", table[idx].name, gpg_strerror (rc)); if (table[idx].special == 1) app->force_chv1 = (valuelen && *value == 0); else if (table[idx].special == 2) parse_login_data (app); return rc; } /* Handle the WRITECERT command for OpenPGP. This rites the standard certifciate to the card; CERTID needs to be set to "OPENPGP.3". PINCB and PINCB_ARG are the usual arguments for the pinentry callback. */ static gpg_error_t do_writecert (app_t app, ctrl_t ctrl, const char *certidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *certdata, size_t certdatalen) { (void)ctrl; #if GNUPG_MAJOR_VERSION > 1 if (strcmp (certidstr, "OPENPGP.3")) return gpg_error (GPG_ERR_INV_ID); if (!certdata || !certdatalen) return gpg_error (GPG_ERR_INV_ARG); if (!app->app_local->extcap.is_v2) return gpg_error (GPG_ERR_NOT_SUPPORTED); if (certdatalen > app->app_local->extcap.max_certlen_3) return gpg_error (GPG_ERR_TOO_LARGE); return do_setattr (app, "CERT-3", pincb, pincb_arg, certdata, certdatalen); #else return gpg_error (GPG_ERR_NOT_IMPLEMENTED); #endif } /* Handle the PASSWD command. The following combinations are possible: Flags CHVNO Vers. Description RESET 1 1 Verify CHV3 and set a new CHV1 and CHV2 RESET 1 2 Verify PW3 and set a new PW1. RESET 2 1 Verify CHV3 and set a new CHV1 and CHV2. RESET 2 2 Verify PW3 and set a new Reset Code. RESET 3 any Returns GPG_ERR_INV_ID. - 1 1 Verify CHV2 and set a new CHV1 and CHV2. - 1 2 Verify PW1 and set a new PW1. - 2 1 Verify CHV2 and set a new CHV1 and CHV2. - 2 2 Verify Reset Code and set a new PW1. - 3 any Verify CHV3/PW3 and set a new CHV3/PW3. */ static gpg_error_t do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int rc = 0; int chvno = atoi (chvnostr); char *resetcode = NULL; char *oldpinvalue = NULL; char *pinvalue; int reset_mode = !!(flags & APP_CHANGE_FLAG_RESET); int set_resetcode = 0; (void)ctrl; if (reset_mode && chvno == 3) { rc = gpg_error (GPG_ERR_INV_ID); goto leave; } if (!app->app_local->extcap.is_v2) { /* Version 1 cards. */ if (reset_mode || chvno == 3) { /* We always require that the PIN is entered. */ app->did_chv3 = 0; rc = verify_chv3 (app, pincb, pincb_arg); if (rc) goto leave; } else if (chvno == 1 || chvno == 2) { /* On a v1.x card CHV1 and CVH2 should always have the same value, thus we enforce it here. */ int save_force = app->force_chv1; app->force_chv1 = 0; app->did_chv1 = 0; app->did_chv2 = 0; rc = verify_chv2 (app, pincb, pincb_arg); app->force_chv1 = save_force; if (rc) goto leave; } else { rc = gpg_error (GPG_ERR_INV_ID); goto leave; } } else { /* Version 2 cards. */ if (reset_mode) { /* To reset a PIN the Admin PIN is required. */ app->did_chv3 = 0; rc = verify_chv3 (app, pincb, pincb_arg); if (rc) goto leave; if (chvno == 2) set_resetcode = 1; } else if (chvno == 1 || chvno == 3) { int minlen = (chvno ==3)? 8 : 6; char *promptbuf = NULL; const char *prompt; if (chvno == 3) { rc = build_enter_admin_pin_prompt (app, &promptbuf); if (rc) goto leave; prompt = promptbuf; } else prompt = _("||Please enter the PIN"); rc = pincb (pincb_arg, prompt, &oldpinvalue); xfree (promptbuf); promptbuf = NULL; if (rc) { log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc)); goto leave; } if (strlen (oldpinvalue) < minlen) { log_info (_("PIN for CHV%d is too short;" " minimum length is %d\n"), chvno, minlen); rc = gpg_error (GPG_ERR_BAD_PIN); goto leave; } } else if (chvno == 2) { /* There is no PW2 for v2 cards. We use this condition to allow a PW reset using the Reset Code. */ void *relptr; unsigned char *value; size_t valuelen; int remaining; int minlen = 8; relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL); if (!relptr || valuelen < 7) { log_error (_("error retrieving CHV status from card\n")); xfree (relptr); rc = gpg_error (GPG_ERR_CARD); goto leave; } remaining = value[5]; xfree (relptr); if (!remaining) { log_error (_("Reset Code not or not anymore available\n")); rc = gpg_error (GPG_ERR_BAD_PIN); goto leave; } rc = pincb (pincb_arg, _("||Please enter the Reset Code for the card"), &resetcode); if (rc) { log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc)); goto leave; } if (strlen (resetcode) < minlen) { log_info (_("Reset Code is too short; minimum length is %d\n"), minlen); rc = gpg_error (GPG_ERR_BAD_PIN); goto leave; } } else { rc = gpg_error (GPG_ERR_INV_ID); goto leave; } } if (chvno == 3) app->did_chv3 = 0; else app->did_chv1 = app->did_chv2 = 0; /* TRANSLATORS: Do not translate the "|*|" prefixes but keep it at the start of the string. We need this elsewhere to get some infos on the string. */ rc = pincb (pincb_arg, set_resetcode? _("|RN|New Reset Code") : chvno == 3? _("|AN|New Admin PIN") : _("|N|New PIN"), &pinvalue); if (rc) { log_error (_("error getting new PIN: %s\n"), gpg_strerror (rc)); goto leave; } if (resetcode) { char *buffer; buffer = xtrymalloc (strlen (resetcode) + strlen (pinvalue) + 1); if (!buffer) rc = gpg_error_from_syserror (); else { strcpy (stpcpy (buffer, resetcode), pinvalue); rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81, buffer, strlen (buffer)); wipememory (buffer, strlen (buffer)); xfree (buffer); } } else if (set_resetcode) { if (strlen (pinvalue) < 8) { log_error (_("Reset Code is too short; minimum length is %d\n"), 8); rc = gpg_error (GPG_ERR_BAD_PIN); } else rc = iso7816_put_data (app->slot, 0, 0xD3, pinvalue, strlen (pinvalue)); } else if (reset_mode) { rc = iso7816_reset_retry_counter (app->slot, 0x81, pinvalue, strlen (pinvalue)); if (!rc && !app->app_local->extcap.is_v2) rc = iso7816_reset_retry_counter (app->slot, 0x82, pinvalue, strlen (pinvalue)); } else if (!app->app_local->extcap.is_v2) { /* Version 1 cards. */ if (chvno == 1 || chvno == 2) { rc = iso7816_change_reference_data (app->slot, 0x81, NULL, 0, pinvalue, strlen (pinvalue)); if (!rc) rc = iso7816_change_reference_data (app->slot, 0x82, NULL, 0, pinvalue, strlen (pinvalue)); } else /* CHVNO == 3 */ { rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, NULL, 0, pinvalue, strlen (pinvalue)); } } else { /* Version 2 cards. */ assert (chvno == 1 || chvno == 3); rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, oldpinvalue, strlen (oldpinvalue), pinvalue, strlen (pinvalue)); } if (pinvalue) { wipememory (pinvalue, strlen (pinvalue)); xfree (pinvalue); } if (rc) flush_cache_after_error (app); leave: if (resetcode) { wipememory (resetcode, strlen (resetcode)); xfree (resetcode); } if (oldpinvalue) { wipememory (oldpinvalue, strlen (oldpinvalue)); xfree (oldpinvalue); } return rc; } /* Check whether a key already exists. KEYIDX is the index of the key (0..2). If FORCE is TRUE a diagnositic will be printed but no error returned if the key already exists. The flag GENERATING is only used to print correct messages. */ static gpg_error_t does_key_exist (app_t app, int keyidx, int generating, int force) { const unsigned char *fpr; unsigned char *buffer; size_t buflen, n; int i; assert (keyidx >=0 && keyidx <= 2); if (iso7816_get_data (app->slot, 0, 0x006E, &buffer, &buflen)) { log_error (_("error reading application data\n")); return gpg_error (GPG_ERR_GENERAL); } fpr = find_tlv (buffer, buflen, 0x00C5, &n); if (!fpr || n < 60) { log_error (_("error reading fingerprint DO\n")); xfree (buffer); return gpg_error (GPG_ERR_GENERAL); } fpr += 20*keyidx; for (i=0; i < 20 && !fpr[i]; i++) ; xfree (buffer); if (i!=20 && !force) { log_error (_("key already exists\n")); return gpg_error (GPG_ERR_EEXIST); } else if (i!=20) log_info (_("existing key will be replaced\n")); else if (generating) log_info (_("generating new key\n")); else log_info (_("writing new key\n")); return 0; } /* Create a TLV tag and value and store it at BUFFER. Return the length of tag and length. A LENGTH greater than 65535 is truncated. */ static size_t add_tlv (unsigned char *buffer, unsigned int tag, size_t length) { unsigned char *p = buffer; assert (tag <= 0xffff); if ( tag > 0xff ) *p++ = tag >> 8; *p++ = tag; if (length < 128) *p++ = length; else if (length < 256) { *p++ = 0x81; *p++ = length; } else { if (length > 0xffff) length = 0xffff; *p++ = 0x82; *p++ = length >> 8; *p++ = length; } return p - buffer; } /* Build the private key template as specified in the OpenPGP specs v2.0 section 4.3.3.7. */ static gpg_error_t build_privkey_template (app_t app, int keyno, const unsigned char *rsa_n, size_t rsa_n_len, const unsigned char *rsa_e, size_t rsa_e_len, const unsigned char *rsa_p, size_t rsa_p_len, const unsigned char *rsa_q, size_t rsa_q_len, unsigned char **result, size_t *resultlen) { size_t rsa_e_reqlen; unsigned char privkey[7*(1+3)]; size_t privkey_len; unsigned char exthdr[2+2+3]; size_t exthdr_len; unsigned char suffix[2+3]; size_t suffix_len; unsigned char *tp; size_t datalen; unsigned char *template; size_t template_size; *result = NULL; *resultlen = 0; switch (app->app_local->keyattr[keyno].format) { case RSA_STD: case RSA_STD_N: break; case RSA_CRT: case RSA_CRT_N: return gpg_error (GPG_ERR_NOT_SUPPORTED); default: return gpg_error (GPG_ERR_INV_VALUE); } /* Get the required length for E. */ rsa_e_reqlen = app->app_local->keyattr[keyno].e_bits/8; assert (rsa_e_len <= rsa_e_reqlen); /* Build the 7f48 cardholder private key template. */ datalen = 0; tp = privkey; tp += add_tlv (tp, 0x91, rsa_e_reqlen); datalen += rsa_e_reqlen; tp += add_tlv (tp, 0x92, rsa_p_len); datalen += rsa_p_len; tp += add_tlv (tp, 0x93, rsa_q_len); datalen += rsa_q_len; if (app->app_local->keyattr[keyno].format == RSA_STD_N || app->app_local->keyattr[keyno].format == RSA_CRT_N) { tp += add_tlv (tp, 0x97, rsa_n_len); datalen += rsa_n_len; } privkey_len = tp - privkey; /* Build the extended header list without the private key template. */ tp = exthdr; *tp++ = keyno ==0 ? 0xb6 : keyno == 1? 0xb8 : 0xa4; *tp++ = 0; tp += add_tlv (tp, 0x7f48, privkey_len); exthdr_len = tp - exthdr; /* Build the 5f48 suffix of the data. */ tp = suffix; tp += add_tlv (tp, 0x5f48, datalen); suffix_len = tp - suffix; /* Now concatenate everything. */ template_size = (1 + 3 /* 0x4d and len. */ + exthdr_len + privkey_len + suffix_len + datalen); tp = template = xtrymalloc_secure (template_size); if (!template) return gpg_error_from_syserror (); tp += add_tlv (tp, 0x4d, exthdr_len + privkey_len + suffix_len + datalen); memcpy (tp, exthdr, exthdr_len); tp += exthdr_len; memcpy (tp, privkey, privkey_len); tp += privkey_len; memcpy (tp, suffix, suffix_len); tp += suffix_len; memcpy (tp, rsa_e, rsa_e_len); if (rsa_e_len < rsa_e_reqlen) { /* Right justify E. */ memmove (tp + rsa_e_reqlen - rsa_e_len, tp, rsa_e_len); memset (tp, 0, rsa_e_reqlen - rsa_e_len); } tp += rsa_e_reqlen; memcpy (tp, rsa_p, rsa_p_len); tp += rsa_p_len; memcpy (tp, rsa_q, rsa_q_len); tp += rsa_q_len; if (app->app_local->keyattr[keyno].format == RSA_STD_N || app->app_local->keyattr[keyno].format == RSA_CRT_N) { memcpy (tp, rsa_n, rsa_n_len); tp += rsa_n_len; } /* Sanity check. We don't know the exact length because we allocated 3 bytes for the first length header. */ assert (tp - template <= template_size); *result = template; *resultlen = tp - template; return 0; } /* Helper for do_writekley to change the size of a key. Not ethat this deletes the entire key without asking. */ static gpg_error_t change_keyattr (app_t app, int keyno, unsigned int nbits, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { gpg_error_t err; unsigned char *buffer; size_t buflen; void *relptr; assert (keyno >=0 && keyno <= 2); if (nbits > 3072) return gpg_error (GPG_ERR_TOO_LARGE); /* Read the current attributes into a buffer. */ relptr = get_one_do (app, 0xC1+keyno, &buffer, &buflen, NULL); if (!relptr) return gpg_error (GPG_ERR_CARD); if (buflen < 6 || buffer[0] != 1) { /* Attriutes too short or not an RSA key. */ xfree (relptr); return gpg_error (GPG_ERR_CARD); } /* We only change n_bits and don't touch anything else. Before we do so, we round up NBITS to a sensible way in the same way as gpg's key generation does it. This may help to sort out problems with a few bits too short keys. */ nbits = ((nbits + 31) / 32) * 32; buffer[1] = (nbits >> 8); buffer[2] = nbits; /* Prepare for storing the key. */ err = verify_chv3 (app, pincb, pincb_arg); if (err) { xfree (relptr); return err; } /* Change the attribute. */ err = iso7816_put_data (app->slot, 0, 0xC1+keyno, buffer, buflen); xfree (relptr); if (err) log_error ("error changing size of key %d to %u bits\n", keyno+1, nbits); else log_info ("size of key %d changed to %u bits\n", keyno+1, nbits); flush_cache (app); parse_algorithm_attribute (app, keyno); app->did_chv1 = 0; app->did_chv2 = 0; app->did_chv3 = 0; return err; } /* Helper to process an setattr command for name KEY-ATTR. It expects a string "--force " in (VALUE,VALUELEN). */ static gpg_error_t change_keyattr_from_string (app_t app, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *value, size_t valuelen) { gpg_error_t err; char *string; int keyno, algo; unsigned int nbits; /* VALUE is expected to be a string but not guaranteed to be terminated. Thus copy it to an allocated buffer first. */ string = xtrymalloc (valuelen+1); if (!string) return gpg_error_from_syserror (); memcpy (string, value, valuelen); string[valuelen] = 0; /* Because this function deletes the key we require the string "--force" in the data to make clear that something serious might happen. */ if (sscanf (string, " --force %d %d %u", &keyno, &algo, &nbits) != 3) err = gpg_error (GPG_ERR_INV_DATA); else if (keyno < 1 || keyno > 3) err = gpg_error (GPG_ERR_INV_ID); else if (algo != 1) err = gpg_error (GPG_ERR_PUBKEY_ALGO); /* Not RSA. */ else if (nbits < 1024) err = gpg_error (GPG_ERR_TOO_SHORT); else err = change_keyattr (app, keyno-1, nbits, pincb, pincb_arg); xfree (string); return err; } /* Handle the WRITEKEY command for OpenPGP. This function expects a canonical encoded S-expression with the secret key in KEYDATA and its length (for assertions) in KEYDATALEN. KEYID needs to be the usual keyid which for OpenPGP is the string "OPENPGP.n" with n=1,2,3. Bit 0 of FLAGS indicates whether an existing key shall get overwritten. PINCB and PINCB_ARG are the usual arguments for the pinentry callback. */ static gpg_error_t do_writekey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const unsigned char *keydata, size_t keydatalen) { gpg_error_t err; int force = (flags & 1); int keyno; const unsigned char *buf, *tok; size_t buflen, toklen; int depth, last_depth1, last_depth2; const unsigned char *rsa_n = NULL; const unsigned char *rsa_e = NULL; const unsigned char *rsa_p = NULL; const unsigned char *rsa_q = NULL; size_t rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len; unsigned int nbits; unsigned int maxbits; unsigned char *template = NULL; unsigned char *tp; size_t template_len; unsigned char fprbuf[20]; u32 created_at = 0; (void)ctrl; if (!strcmp (keyid, "OPENPGP.1")) keyno = 0; else if (!strcmp (keyid, "OPENPGP.2")) keyno = 1; else if (!strcmp (keyid, "OPENPGP.3")) keyno = 2; else return gpg_error (GPG_ERR_INV_ID); err = does_key_exist (app, keyno, 0, force); if (err) return err; /* Parse the S-expression */ buf = keydata; buflen = keydatalen; depth = 0; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if (!tok || toklen != 11 || memcmp ("private-key", tok, toklen)) { if (!tok) ; else if (toklen == 21 && !memcmp ("protected-private-key", tok, toklen)) log_info ("protected-private-key passed to writekey\n"); else if (toklen == 20 && !memcmp ("shadowed-private-key", tok, toklen)) log_info ("shadowed-private-key passed to writekey\n"); err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen)) { err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); goto leave; } last_depth1 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth1) { if (tok) { err = gpg_error (GPG_ERR_UNKNOWN_SEXP); goto leave; } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if (tok && toklen == 1) { const unsigned char **mpi; size_t *mpi_len; switch (*tok) { case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break; case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break; case 'p': mpi = &rsa_p; mpi_len = &rsa_p_len; break; case 'q': mpi = &rsa_q; mpi_len = &rsa_q_len;break; default: mpi = NULL; mpi_len = NULL; break; } if (mpi && *mpi) { err = gpg_error (GPG_ERR_DUP_VALUE); goto leave; } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if (tok && mpi) { /* Strip off leading zero bytes and save. */ for (;toklen && !*tok; toklen--, tok++) ; *mpi = tok; *mpi_len = toklen; } } /* Skip until end of list. */ last_depth2 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth2) ; if (err) goto leave; } /* Parse other attributes. */ last_depth1 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth1) { if (tok) { err = gpg_error (GPG_ERR_UNKNOWN_SEXP); goto leave; } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) goto leave; if (tok && toklen == 10 && !memcmp ("created-at", tok, toklen)) { if ((err = parse_sexp (&buf,&buflen,&depth,&tok,&toklen))) goto leave; if (tok) { for (created_at=0; toklen && *tok && *tok >= '0' && *tok <= '9'; tok++, toklen--) created_at = created_at*10 + (*tok - '0'); } } /* Skip until end of list. */ last_depth2 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth2) ; if (err) goto leave; } /* Check that we have all parameters and that they match the card description. */ if (!created_at) { log_error (_("creation timestamp missing\n")); err = gpg_error (GPG_ERR_INV_VALUE); goto leave; } maxbits = app->app_local->keyattr[keyno].n_bits; nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0; if (opt.verbose) log_info ("RSA modulus size is %u bits (%u bytes)\n", nbits, (unsigned int)rsa_n_len); if (nbits && nbits != maxbits && app->app_local->extcap.algo_attr_change) { /* Try to switch the key to a new length. */ err = change_keyattr (app, keyno, nbits, pincb, pincb_arg); if (!err) maxbits = app->app_local->keyattr[keyno].n_bits; } if (nbits != maxbits) { log_error (_("RSA modulus missing or not of size %d bits\n"), (int)maxbits); err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; } maxbits = app->app_local->keyattr[keyno].e_bits; if (maxbits > 32 && !app->app_local->extcap.is_v2) maxbits = 32; /* Our code for v1 does only support 32 bits. */ nbits = rsa_e? count_bits (rsa_e, rsa_e_len) : 0; if (nbits < 2 || nbits > maxbits) { log_error (_("RSA public exponent missing or larger than %d bits\n"), (int)maxbits); err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; } maxbits = app->app_local->keyattr[keyno].n_bits/2; nbits = rsa_p? count_bits (rsa_p, rsa_p_len) : 0; if (nbits != maxbits) { log_error (_("RSA prime %s missing or not of size %d bits\n"), "P", (int)maxbits); err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; } nbits = rsa_q? count_bits (rsa_q, rsa_q_len) : 0; if (nbits != maxbits) { log_error (_("RSA prime %s missing or not of size %d bits\n"), "Q", (int)maxbits); err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; } /* We need to remove the cached public key. */ xfree (app->app_local->pk[keyno].key); app->app_local->pk[keyno].key = NULL; app->app_local->pk[keyno].keylen = 0; app->app_local->pk[keyno].read_done = 0; if (app->app_local->extcap.is_v2) { /* Build the private key template as described in section 4.3.3.7 of the OpenPGP card specs version 2.0. */ int exmode; err = build_privkey_template (app, keyno, rsa_n, rsa_n_len, rsa_e, rsa_e_len, rsa_p, rsa_p_len, rsa_q, rsa_q_len, &template, &template_len); if (err) goto leave; /* Prepare for storing the key. */ err = verify_chv3 (app, pincb, pincb_arg); if (err) goto leave; /* Store the key. */ if (app->app_local->cardcap.ext_lc_le && template_len > 254) exmode = 1; /* Use extended length w/o a limit. */ else if (app->app_local->cardcap.cmd_chaining && template_len > 254) exmode = -254; else exmode = 0; err = iso7816_put_data_odd (app->slot, exmode, 0x3fff, template, template_len); } else { /* Build the private key template as described in section 4.3.3.6 of the OpenPGP card specs version 1.1: 0xC0 public exponent 0xC1 prime p 0xC2 prime q */ assert (rsa_e_len <= 4); template_len = (1 + 1 + 4 + 1 + 1 + rsa_p_len + 1 + 1 + rsa_q_len); template = tp = xtrymalloc_secure (template_len); if (!template) { err = gpg_error_from_syserror (); goto leave; } *tp++ = 0xC0; *tp++ = 4; memcpy (tp, rsa_e, rsa_e_len); if (rsa_e_len < 4) { /* Right justify E. */ memmove (tp+4-rsa_e_len, tp, rsa_e_len); memset (tp, 0, 4-rsa_e_len); } tp += 4; *tp++ = 0xC1; *tp++ = rsa_p_len; memcpy (tp, rsa_p, rsa_p_len); tp += rsa_p_len; *tp++ = 0xC2; *tp++ = rsa_q_len; memcpy (tp, rsa_q, rsa_q_len); tp += rsa_q_len; assert (tp - template == template_len); /* Prepare for storing the key. */ err = verify_chv3 (app, pincb, pincb_arg); if (err) goto leave; /* Store the key. */ err = iso7816_put_data (app->slot, 0, (app->card_version > 0x0007? 0xE0:0xE9)+keyno, template, template_len); } if (err) { log_error (_("failed to store the key: %s\n"), gpg_strerror (err)); goto leave; } err = store_fpr (app, keyno, created_at, rsa_n, rsa_n_len, rsa_e, rsa_e_len, fprbuf, app->card_version); if (err) goto leave; leave: xfree (template); return err; } /* Handle the GENKEY command. */ static gpg_error_t do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, time_t createtime, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { int rc; char numbuf[30]; unsigned char fprbuf[20]; const unsigned char *keydata, *m, *e; unsigned char *buffer = NULL; size_t buflen, keydatalen, mlen, elen; time_t created_at; int keyno = atoi (keynostr); int force = (flags & 1); time_t start_at; int exmode; int le_value; unsigned int keybits; if (keyno < 1 || keyno > 3) return gpg_error (GPG_ERR_INV_ID); keyno--; /* We flush the cache to increase the traffic before a key generation. This _might_ help a card to gather more entropy. */ flush_cache (app); /* Obviously we need to remove the cached public key. */ xfree (app->app_local->pk[keyno].key); app->app_local->pk[keyno].key = NULL; app->app_local->pk[keyno].keylen = 0; app->app_local->pk[keyno].read_done = 0; /* Check whether a key already exists. */ rc = does_key_exist (app, keyno, 1, force); if (rc) return rc; /* Because we send the key parameter back via status lines we need to put a limit on the max. allowed keysize. 2048 bit will already lead to a 527 byte long status line and thus a 4096 bit key would exceed the Assuan line length limit. */ keybits = app->app_local->keyattr[keyno].n_bits; if (keybits > 3072) return gpg_error (GPG_ERR_TOO_LARGE); /* Prepare for key generation by verifying the Admin PIN. */ rc = verify_chv3 (app, pincb, pincb_arg); if (rc) goto leave; /* Test whether we will need extended length mode. (1900 is an arbitrary length which for sure fits into a short apdu.) */ if (app->app_local->cardcap.ext_lc_le && keybits > 1900) { exmode = 1; /* Use extended length w/o a limit. */ le_value = app->app_local->extcap.max_rsp_data; /* No need to check le_value because it comes from a 16 bit value and thus can't create an overflow on a 32 bit system. */ } else { exmode = 0; le_value = 256; /* Use legacy value. */ } log_info (_("please wait while key is being generated ...\n")); start_at = time (NULL); rc = iso7816_generate_keypair /* # warning key generation temporary replaced by reading an existing key. */ /* rc = iso7816_read_public_key */ (app->slot, exmode, (const unsigned char*)(keyno == 0? "\xB6" : keyno == 1? "\xB8" : "\xA4"), 2, le_value, &buffer, &buflen); if (rc) { rc = gpg_error (GPG_ERR_CARD); log_error (_("generating key failed\n")); goto leave; } log_info (_("key generation completed (%d seconds)\n"), (int)(time (NULL) - start_at)); keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen); if (!keydata) { rc = gpg_error (GPG_ERR_CARD); log_error (_("response does not contain the public key data\n")); goto leave; } m = find_tlv (keydata, keydatalen, 0x0081, &mlen); if (!m) { rc = gpg_error (GPG_ERR_CARD); log_error (_("response does not contain the RSA modulus\n")); goto leave; } /* log_printhex ("RSA n:", m, mlen); */ send_key_data (ctrl, "n", m, mlen); e = find_tlv (keydata, keydatalen, 0x0082, &elen); if (!e) { rc = gpg_error (GPG_ERR_CARD); log_error (_("response does not contain the RSA public exponent\n")); goto leave; } /* log_printhex ("RSA e:", e, elen); */ send_key_data (ctrl, "e", e, elen); created_at = createtime? createtime : gnupg_get_time (); sprintf (numbuf, "%lu", (unsigned long)created_at); send_status_info (ctrl, "KEY-CREATED-AT", numbuf, (size_t)strlen(numbuf), NULL, 0); rc = store_fpr (app, keyno, (u32)created_at, m, mlen, e, elen, fprbuf, app->card_version); if (rc) goto leave; send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf); leave: xfree (buffer); return rc; } static unsigned long convert_sig_counter_value (const unsigned char *value, size_t valuelen) { unsigned long ul; if (valuelen == 3 ) ul = (value[0] << 16) | (value[1] << 8) | value[2]; else { log_error (_("invalid structure of OpenPGP card (DO 0x93)\n")); ul = 0; } return ul; } static unsigned long get_sig_counter (app_t app) { void *relptr; unsigned char *value; size_t valuelen; unsigned long ul; relptr = get_one_do (app, 0x0093, &value, &valuelen, NULL); if (!relptr) return 0; ul = convert_sig_counter_value (value, valuelen); xfree (relptr); return ul; } static gpg_error_t compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr) { const unsigned char *fpr; unsigned char *buffer; size_t buflen, n; int rc, i; assert (keyno >= 1 && keyno <= 3); rc = get_cached_data (app, 0x006E, &buffer, &buflen, 0, 0); if (rc) { log_error (_("error reading application data\n")); return gpg_error (GPG_ERR_GENERAL); } fpr = find_tlv (buffer, buflen, 0x00C5, &n); if (!fpr || n != 60) { xfree (buffer); log_error (_("error reading fingerprint DO\n")); return gpg_error (GPG_ERR_GENERAL); } fpr += (keyno-1)*20; for (i=0; i < 20; i++) if (sha1fpr[i] != fpr[i]) { xfree (buffer); log_info (_("fingerprint on card does not match requested one\n")); return gpg_error (GPG_ERR_WRONG_SECKEY); } xfree (buffer); return 0; } /* If a fingerprint has been specified check it against the one on the card. This allows for a meaningful error message in case the key on the card has been replaced but the shadow information known to gpg has not been updated. If there is no fingerprint we assume that this is okay. */ static gpg_error_t check_against_given_fingerprint (app_t app, const char *fpr, int keyno) { unsigned char tmp[20]; const char *s; int n; for (s=fpr, n=0; hexdigitp (s); s++, n++) ; if (n != 40) return gpg_error (GPG_ERR_INV_ID); else if (!*s) ; /* okay */ else return gpg_error (GPG_ERR_INV_ID); for (s=fpr, n=0; n < 20; s += 2, n++) tmp[n] = xtoi_2 (s); return compare_fingerprint (app, keyno, tmp); } /* Compute a digital signature on INDATA which is expected to be the raw message digest. For this application the KEYIDSTR consists of the serialnumber and the fingerprint delimited by a slash. Note that this function may return the error code GPG_ERR_WRONG_CARD to indicate that the card currently present does not match the one required for the requested action (e.g. the serial number does not match). As a special feature a KEYIDSTR of "OPENPGP.3" redirects the operation to the auth command. */ static gpg_error_t do_sign (app_t app, const char *keyidstr, int hashalgo, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) { static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 }; static unsigned char sha1_prefix[15] = /* (1.3.14.3.2.26) */ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 }; static unsigned char sha224_prefix[19] = /* (2.16.840.1.101.3.4.2.4) */ { 0x30, 0x2D, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1C }; static unsigned char sha256_prefix[19] = /* (2.16.840.1.101.3.4.2.1) */ { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 }; static unsigned char sha384_prefix[19] = /* (2.16.840.1.101.3.4.2.2) */ { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 }; static unsigned char sha512_prefix[19] = /* (2.16.840.1.101.3.4.2.3) */ { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 }; int rc; unsigned char data[19+64]; size_t datalen; unsigned char tmp_sn[20]; /* Actually 16 bytes but also for the fpr. */ const char *s; int n; const char *fpr = NULL; unsigned long sigcount; int use_auth = 0; int exmode, le_value; if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); /* Strip off known prefixes. */ #define X(a,b,c,d) \ if (hashalgo == GCRY_MD_ ## a \ && (d) \ && indatalen == sizeof b ## _prefix + (c) \ && !memcmp (indata, b ## _prefix, sizeof b ## _prefix)) \ { \ indata = (const char*)indata + sizeof b ## _prefix; \ indatalen -= sizeof b ## _prefix; \ } if (indatalen == 20) ; /* Assume a plain SHA-1 or RMD160 digest has been given. */ else X(SHA1, sha1, 20, 1) else X(RMD160, rmd160, 20, 1) else X(SHA224, sha224, 28, app->app_local->extcap.is_v2) else X(SHA256, sha256, 32, app->app_local->extcap.is_v2) else X(SHA384, sha384, 48, app->app_local->extcap.is_v2) else X(SHA512, sha512, 64, app->app_local->extcap.is_v2) else if ((indatalen == 28 || indatalen == 32 || indatalen == 48 || indatalen ==64) && app->app_local->extcap.is_v2) ; /* Assume a plain SHA-3 digest has been given. */ else { log_error (_("card does not support digest algorithm %s\n"), gcry_md_algo_name (hashalgo)); /* Or the supplied digest length does not match an algorithm. */ return gpg_error (GPG_ERR_INV_VALUE); } #undef X /* Check whether an OpenPGP card of any version has been requested. */ if (!strcmp (keyidstr, "OPENPGP.1")) ; else if (!strcmp (keyidstr, "OPENPGP.3")) use_auth = 1; else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12)) return gpg_error (GPG_ERR_INV_ID); else { for (s=keyidstr, n=0; hexdigitp (s); s++, n++) ; if (n != 32) return gpg_error (GPG_ERR_INV_ID); else if (!*s) ; /* no fingerprint given: we allow this for now. */ else if (*s == '/') fpr = s + 1; else return gpg_error (GPG_ERR_INV_ID); for (s=keyidstr, n=0; n < 16; s += 2, n++) tmp_sn[n] = xtoi_2 (s); if (app->serialnolen != 16) return gpg_error (GPG_ERR_INV_CARD); if (memcmp (app->serialno, tmp_sn, 16)) return gpg_error (GPG_ERR_WRONG_CARD); } /* If a fingerprint has been specified check it against the one on the card. This is allows for a meaningful error message in case the key on the card has been replaced but the shadow information known to gpg was not updated. If there is no fingerprint, gpg will detect a bogus signature anyway due to the verify-after-signing feature. */ rc = fpr? check_against_given_fingerprint (app, fpr, 1) : 0; if (rc) return rc; /* Concatenate prefix and digest. */ #define X(a,b,d) \ if (hashalgo == GCRY_MD_ ## a && (d) ) \ { \ datalen = sizeof b ## _prefix + indatalen; \ assert (datalen <= sizeof data); \ memcpy (data, b ## _prefix, sizeof b ## _prefix); \ memcpy (data + sizeof b ## _prefix, indata, indatalen); \ } X(SHA1, sha1, 1) else X(RMD160, rmd160, 1) else X(SHA224, sha224, app->app_local->extcap.is_v2) else X(SHA256, sha256, app->app_local->extcap.is_v2) else X(SHA384, sha384, app->app_local->extcap.is_v2) else X(SHA512, sha512, app->app_local->extcap.is_v2) else return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); #undef X /* Redirect to the AUTH command if asked to. */ if (use_auth) { return do_auth (app, "OPENPGP.3", pincb, pincb_arg, data, datalen, outdata, outdatalen); } /* Show the number of signature done using this key. */ sigcount = get_sig_counter (app); log_info (_("signatures created so far: %lu\n"), sigcount); /* Check CHV if needed. */ if (!app->did_chv1 || app->force_chv1 ) { char *pinvalue; rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, &pinvalue); if (rc) return rc; app->did_chv1 = 1; /* For cards with versions < 2 we want to keep CHV1 and CHV2 in sync, thus we verify CHV2 here using the given PIN. Cards with version2 to not have the need for a separate CHV2 and internally use just one. Obviously we can't do that if the keypad has been used. */ if (!app->did_chv2 && pinvalue && !app->app_local->extcap.is_v2) { rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue)); if (gpg_err_code (rc) == GPG_ERR_BAD_PIN) rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED); if (rc) { log_error (_("verify CHV%d failed: %s\n"), 2, gpg_strerror (rc)); xfree (pinvalue); flush_cache_after_error (app); return rc; } app->did_chv2 = 1; } xfree (pinvalue); } if (app->app_local->cardcap.ext_lc_le) { exmode = 1; /* Use extended length. */ le_value = app->app_local->extcap.max_rsp_data; } else { exmode = 0; le_value = 0; } rc = iso7816_compute_ds (app->slot, exmode, data, datalen, le_value, outdata, outdatalen); return rc; } /* Compute a digital signature using the INTERNAL AUTHENTICATE command on INDATA which is expected to be the raw message digest. For this application the KEYIDSTR consists of the serialnumber and the fingerprint delimited by a slash. Optionally the id OPENPGP.3 may be given. Note that this function may return the error code GPG_ERR_WRONG_CARD to indicate that the card currently present does not match the one required for the requested action (e.g. the serial number does not match). */ static gpg_error_t do_auth (app_t app, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) { int rc; unsigned char tmp_sn[20]; /* Actually 16 but we use it also for the fpr. */ const char *s; int n; const char *fpr = NULL; if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); if (indatalen > 101) /* For a 2048 bit key. */ return gpg_error (GPG_ERR_INV_VALUE); /* Check whether an OpenPGP card of any version has been requested. */ if (!strcmp (keyidstr, "OPENPGP.3")) ; else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12)) return gpg_error (GPG_ERR_INV_ID); else { for (s=keyidstr, n=0; hexdigitp (s); s++, n++) ; if (n != 32) return gpg_error (GPG_ERR_INV_ID); else if (!*s) ; /* no fingerprint given: we allow this for now. */ else if (*s == '/') fpr = s + 1; else return gpg_error (GPG_ERR_INV_ID); for (s=keyidstr, n=0; n < 16; s += 2, n++) tmp_sn[n] = xtoi_2 (s); if (app->serialnolen != 16) return gpg_error (GPG_ERR_INV_CARD); if (memcmp (app->serialno, tmp_sn, 16)) return gpg_error (GPG_ERR_WRONG_CARD); } /* If a fingerprint has been specified check it against the one on the card. This is allows for a meaningful error message in case the key on the card has been replaced but the shadow information known to gpg was not updated. If there is no fingerprint, gpg will detect a bogus signature anyway due to the verify-after-signing feature. */ rc = fpr? check_against_given_fingerprint (app, fpr, 3) : 0; if (rc) return rc; rc = verify_chv2 (app, pincb, pincb_arg); if (!rc) { int exmode, le_value; if (app->app_local->cardcap.ext_lc_le) { exmode = 1; /* Use extended length. */ le_value = app->app_local->extcap.max_rsp_data; } else { exmode = 0; le_value = 0; } rc = iso7816_internal_authenticate (app->slot, exmode, indata, indatalen, le_value, outdata, outdatalen); } return rc; } static gpg_error_t do_decipher (app_t app, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ) { int rc; unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */ const char *s; int n; const char *fpr = NULL; int exmode, le_value; if (!keyidstr || !*keyidstr || !indatalen) return gpg_error (GPG_ERR_INV_VALUE); /* Check whether an OpenPGP card of any version has been requested. */ if (!strcmp (keyidstr, "OPENPGP.2")) ; else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12)) return gpg_error (GPG_ERR_INV_ID); else { for (s=keyidstr, n=0; hexdigitp (s); s++, n++) ; if (n != 32) return gpg_error (GPG_ERR_INV_ID); else if (!*s) ; /* no fingerprint given: we allow this for now. */ else if (*s == '/') fpr = s + 1; else return gpg_error (GPG_ERR_INV_ID); for (s=keyidstr, n=0; n < 16; s += 2, n++) tmp_sn[n] = xtoi_2 (s); if (app->serialnolen != 16) return gpg_error (GPG_ERR_INV_CARD); if (memcmp (app->serialno, tmp_sn, 16)) return gpg_error (GPG_ERR_WRONG_CARD); } /* If a fingerprint has been specified check it against the one on the card. This is allows for a meaningful error message in case the key on the card has been replaced but the shadow information known to gpg was not updated. If there is no fingerprint, the decryption won't produce the right plaintext anyway. */ rc = fpr? check_against_given_fingerprint (app, fpr, 2) : 0; if (rc) return rc; rc = verify_chv2 (app, pincb, pincb_arg); if (!rc) { size_t fixuplen; unsigned char *fixbuf = NULL; int padind = 0; /* We might encounter a couple of leading zeroes in the cryptogram. Due to internal use of MPIs thease leading zeroes are stripped. However the OpenPGP card expects exactly 128 bytes for the cryptogram (for a 1k key). Thus we need to fix it up. We do this for up to 16 leading zero bytes; a cryptogram with more than this is with a very high probability anyway broken. */ if (indatalen >= (128-16) && indatalen < 128) /* 1024 bit key. */ fixuplen = 128 - indatalen; else if (indatalen >= (192-16) && indatalen < 192) /* 1536 bit key. */ fixuplen = 192 - indatalen; else if (indatalen >= (256-16) && indatalen < 256) /* 2048 bit key. */ fixuplen = 256 - indatalen; else if (indatalen >= (384-16) && indatalen < 384) /* 3072 bit key. */ fixuplen = 384 - indatalen; else fixuplen = 0; if (fixuplen) { /* While we have to prepend stuff anyway, we can also include the padding byte here so that iso1816_decipher does not need to do another data mangling. */ fixuplen++; fixbuf = xtrymalloc (fixuplen + indatalen); if (!fixbuf) return gpg_error_from_syserror (); memset (fixbuf, 0, fixuplen); memcpy (fixbuf+fixuplen, indata, indatalen); indata = fixbuf; indatalen = fixuplen + indatalen; padind = -1; /* Already padded. */ } if (app->app_local->cardcap.ext_lc_le && indatalen > 254 ) { exmode = 1; /* Extended length w/o a limit. */ le_value = app->app_local->extcap.max_rsp_data; } else if (app->app_local->cardcap.cmd_chaining && indatalen > 254) { exmode = -254; /* Command chaining with max. 254 bytes. */ le_value = 0; } else exmode = le_value = 0; rc = iso7816_decipher (app->slot, exmode, indata, indatalen, le_value, padind, outdata, outdatalen); xfree (fixbuf); } return rc; } /* Perform a simple verify operation for CHV1 and CHV2, so that further operations won't ask for CHV2 and it is possible to do a cheap check on the PIN: If there is something wrong with the PIN entry system, only the regular CHV will get blocked and not the dangerous CHV3. KEYIDSTR is the usual card's serial number; an optional fingerprint part will be ignored. There is a special mode if the keyidstr is "[CHV3]" with the "[CHV3]" being a literal string: The Admin Pin is checked if and only if the retry counter is still at 3. */ static gpg_error_t do_check_pin (app_t app, const char *keyidstr, gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg) { unsigned char tmp_sn[20]; const char *s; int n; int admin_pin = 0; if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); /* Check whether an OpenPGP card of any version has been requested. */ if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12)) return gpg_error (GPG_ERR_INV_ID); for (s=keyidstr, n=0; hexdigitp (s); s++, n++) ; if (n != 32) return gpg_error (GPG_ERR_INV_ID); else if (!*s) ; /* No fingerprint given: we allow this for now. */ else if (*s == '/') ; /* We ignore a fingerprint. */ else if (!strcmp (s, "[CHV3]") ) admin_pin = 1; else return gpg_error (GPG_ERR_INV_ID); for (s=keyidstr, n=0; n < 16; s += 2, n++) tmp_sn[n] = xtoi_2 (s); if (app->serialnolen != 16) return gpg_error (GPG_ERR_INV_CARD); if (memcmp (app->serialno, tmp_sn, 16)) return gpg_error (GPG_ERR_WRONG_CARD); /* Yes, there is a race conditions: The user might pull the card right here and we won't notice that. However this is not a problem and the check above is merely for a graceful failure between operations. */ if (admin_pin) { void *relptr; unsigned char *value; size_t valuelen; int count; relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL); if (!relptr || valuelen < 7) { log_error (_("error retrieving CHV status from card\n")); xfree (relptr); return gpg_error (GPG_ERR_CARD); } count = value[6]; xfree (relptr); if (!count) { log_info (_("card is permanently locked!\n")); return gpg_error (GPG_ERR_BAD_PIN); } else if (count < 3) { log_info (_("verification of Admin PIN is currently prohibited " "through this command\n")); return gpg_error (GPG_ERR_GENERAL); } app->did_chv3 = 0; /* Force verification. */ return verify_chv3 (app, pincb, pincb_arg); } else return verify_chv2 (app, pincb, pincb_arg); } /* Show information about card capabilities. */ static void show_caps (struct app_local_s *s) { log_info ("Version-2 ......: %s\n", s->extcap.is_v2? "yes":"no"); log_info ("Get-Challenge ..: %s", s->extcap.get_challenge? "yes":"no"); if (s->extcap.get_challenge) log_printf (" (%u bytes max)", s->extcap.max_get_challenge); log_info ("Key-Import .....: %s\n", s->extcap.key_import? "yes":"no"); log_info ("Change-Force-PW1: %s\n", s->extcap.change_force_chv? "yes":"no"); log_info ("Private-DOs ....: %s\n", s->extcap.private_dos? "yes":"no"); log_info ("Algo-Attr-Change: %s\n", s->extcap.algo_attr_change? "yes":"no"); log_info ("SM-Support .....: %s", s->extcap.sm_supported? "yes":"no"); if (s->extcap.sm_supported) log_printf (" (%s)", s->extcap.sm_aes128? "AES-128":"3DES"); log_info ("Max-Cert3-Len ..: %u\n", s->extcap.max_certlen_3); log_info ("Max-Cmd-Data ...: %u\n", s->extcap.max_cmd_data); log_info ("Max-Rsp-Data ...: %u\n", s->extcap.max_rsp_data); log_info ("Cmd-Chaining ...: %s\n", s->cardcap.cmd_chaining?"yes":"no"); log_info ("Ext-Lc-Le ......: %s\n", s->cardcap.ext_lc_le?"yes":"no"); log_info ("Status Indicator: %02X\n", s->status_indicator); log_info ("GnuPG-No-Sync ..: %s\n", s->flags.no_sync? "yes":"no"); log_info ("GnuPG-Def-PW2 ..: %s\n", s->flags.def_chv2? "yes":"no"); } /* Parse the historical bytes in BUFFER of BUFLEN and store them in APPLOC. */ static void parse_historical (struct app_local_s *apploc, const unsigned char * buffer, size_t buflen) { /* Example buffer: 00 31 C5 73 C0 01 80 00 90 00 */ if (buflen < 4) { log_error ("warning: historical bytes are too short\n"); return; /* Too short. */ } if (*buffer) { log_error ("warning: bad category indicator in historical bytes\n"); return; } /* Skip category indicator. */ buffer++; buflen--; /* Get the status indicator. */ apploc->status_indicator = buffer[buflen-3]; buflen -= 3; /* Parse the compact TLV. */ while (buflen) { unsigned int tag = (*buffer & 0xf0) >> 4; unsigned int len = (*buffer & 0x0f); if (len+1 > buflen) { log_error ("warning: bad Compact-TLV in historical bytes\n"); return; /* Error. */ } buffer++; buflen--; if (tag == 7 && len == 3) { /* Card capabilities. */ apploc->cardcap.cmd_chaining = !!(buffer[2] & 0x80); apploc->cardcap.ext_lc_le = !!(buffer[2] & 0x40); } buffer += len; buflen -= len; } } /* Parse and optionally show the algorithm attributes for KEYNO. KEYNO must be in the range 0..2. */ static void parse_algorithm_attribute (app_t app, int keyno) { unsigned char *buffer; size_t buflen; void *relptr; const char desc[3][5] = {"sign", "encr", "auth"}; assert (keyno >=0 && keyno <= 2); app->app_local->keyattr[keyno].n_bits = 0; relptr = get_one_do (app, 0xC1+keyno, &buffer, &buflen, NULL); if (!relptr) { log_error ("error reading DO 0x%02X\n", 0xc1+keyno); return; } if (buflen < 1) { log_error ("error reading DO 0x%02X\n", 0xc1+keyno); xfree (relptr); return; } if (opt.verbose) log_info ("Key-Attr-%s ..: ", desc[keyno]); if (*buffer == 1 && (buflen == 5 || buflen == 6)) { app->app_local->keyattr[keyno].n_bits = (buffer[1]<<8 | buffer[2]); app->app_local->keyattr[keyno].e_bits = (buffer[3]<<8 | buffer[4]); app->app_local->keyattr[keyno].format = 0; if (buflen < 6) app->app_local->keyattr[keyno].format = RSA_STD; else app->app_local->keyattr[keyno].format = (buffer[5] == 0? RSA_STD : buffer[5] == 1? RSA_STD_N : buffer[5] == 2? RSA_CRT : buffer[5] == 3? RSA_CRT_N : RSA_UNKNOWN_FMT); if (opt.verbose) log_printf ("RSA, n=%u, e=%u, fmt=%s\n", app->app_local->keyattr[keyno].n_bits, app->app_local->keyattr[keyno].e_bits, app->app_local->keyattr[keyno].format == RSA_STD? "std" : app->app_local->keyattr[keyno].format == RSA_STD_N?"std+n": app->app_local->keyattr[keyno].format == RSA_CRT? "crt" : app->app_local->keyattr[keyno].format == RSA_CRT_N?"crt+n":"?"); } else if (opt.verbose) log_printhex ("", buffer, buflen); xfree (relptr); } /* Select the OpenPGP application on the card in SLOT. This function must be used before any other OpenPGP application functions. */ gpg_error_t app_select_openpgp (app_t app) { static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 }; int slot = app->slot; int rc; unsigned char *buffer; size_t buflen; void *relptr; /* Note that the card can't cope with P2=0xCO, thus we need to pass a special flag value. */ rc = iso7816_select_application (slot, aid, sizeof aid, 0x0001); if (!rc) { unsigned int manufacturer; app->apptype = "OPENPGP"; app->did_chv1 = 0; app->did_chv2 = 0; app->did_chv3 = 0; app->app_local = NULL; /* The OpenPGP card returns the serial number as part of the AID; because we prefer to use OpenPGP serial numbers, we replace a possibly already set one from a EF.GDO with this one. Note, that for current OpenPGP cards, no EF.GDO exists and thus it won't matter at all. */ rc = iso7816_get_data (slot, 0, 0x004F, &buffer, &buflen); if (rc) goto leave; if (opt.verbose) { log_info ("AID: "); log_printhex ("", buffer, buflen); } app->card_version = buffer[6] << 8; app->card_version |= buffer[7]; manufacturer = (buffer[8]<<8 | buffer[9]); xfree (app->serialno); app->serialno = buffer; app->serialnolen = buflen; buffer = NULL; app->app_local = xtrycalloc (1, sizeof *app->app_local); if (!app->app_local) { rc = gpg_error (gpg_err_code_from_errno (errno)); goto leave; } if (app->card_version >= 0x0200) app->app_local->extcap.is_v2 = 1; /* Read the historical bytes. */ relptr = get_one_do (app, 0x5f52, &buffer, &buflen, NULL); if (relptr) { if (opt.verbose) { log_info ("Historical Bytes: "); log_printhex ("", buffer, buflen); } parse_historical (app->app_local, buffer, buflen); xfree (relptr); } /* Read the force-chv1 flag. */ relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL); if (!relptr) { log_error (_("can't access %s - invalid OpenPGP card?\n"), "CHV Status Bytes"); goto leave; } app->force_chv1 = (buflen && *buffer == 0); xfree (relptr); /* Read the extended capabilities. */ relptr = get_one_do (app, 0x00C0, &buffer, &buflen, NULL); if (!relptr) { log_error (_("can't access %s - invalid OpenPGP card?\n"), "Extended Capability Flags" ); goto leave; } if (buflen) { app->app_local->extcap.sm_supported = !!(*buffer & 0x80); app->app_local->extcap.get_challenge = !!(*buffer & 0x40); app->app_local->extcap.key_import = !!(*buffer & 0x20); app->app_local->extcap.change_force_chv = !!(*buffer & 0x10); app->app_local->extcap.private_dos = !!(*buffer & 0x08); app->app_local->extcap.algo_attr_change = !!(*buffer & 0x04); } if (buflen >= 10) { /* Available with v2 cards. */ app->app_local->extcap.sm_aes128 = (buffer[1] == 1); app->app_local->extcap.max_get_challenge = (buffer[2] << 8 | buffer[3]); app->app_local->extcap.max_certlen_3 = (buffer[4] << 8 | buffer[5]); app->app_local->extcap.max_cmd_data = (buffer[6] << 8 | buffer[7]); app->app_local->extcap.max_rsp_data = (buffer[8] << 8 | buffer[9]); } xfree (relptr); /* Some of the first cards accidently don't set the CHANGE_FORCE_CHV bit but allow it anyway. */ if (app->card_version <= 0x0100 && manufacturer == 1) app->app_local->extcap.change_force_chv = 1; parse_login_data (app); if (opt.verbose) show_caps (app->app_local); parse_algorithm_attribute (app, 0); parse_algorithm_attribute (app, 1); parse_algorithm_attribute (app, 2); if (opt.verbose > 1) dump_all_do (slot); app->fnc.deinit = do_deinit; app->fnc.learn_status = do_learn_status; app->fnc.readcert = do_readcert; app->fnc.readkey = do_readkey; app->fnc.getattr = do_getattr; app->fnc.setattr = do_setattr; app->fnc.writecert = do_writecert; app->fnc.writekey = do_writekey; app->fnc.genkey = do_genkey; app->fnc.sign = do_sign; app->fnc.auth = do_auth; app->fnc.decipher = do_decipher; app->fnc.change_pin = do_change_pin; app->fnc.check_pin = do_check_pin; } leave: if (rc) do_deinit (app); return rc; } diff --git a/g10/build-packet.c b/g10/build-packet.c index 499dd68db..60eb3c8f1 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -1,1316 +1,1316 @@ /* build-packet.c - assemble packets and write them * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, * 2006 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 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include "packet.h" #include "errors.h" #include "iobuf.h" #include "mpi.h" #include "util.h" #include "cipher.h" #include "memory.h" #include "i18n.h" #include "options.h" +#include "../include/host2net.h" static int do_user_id( IOBUF out, int ctb, PKT_user_id *uid ); static int do_public_key( IOBUF out, int ctb, PKT_public_key *pk ); static int do_secret_key( IOBUF out, int ctb, PKT_secret_key *pk ); static int do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ); static int do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ); static u32 calc_plaintext( PKT_plaintext *pt ); static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt ); static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed ); static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ); static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd ); static int do_signature( IOBUF out, int ctb, PKT_signature *sig ); static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops ); static int calc_header_length( u32 len, int new_ctb ); static int write_16(IOBUF inp, u16 a); static int write_32(IOBUF inp, u32 a); static int write_header( IOBUF out, int ctb, u32 len ); static int write_sign_packet_header( IOBUF out, int ctb, u32 len ); static int write_header2( IOBUF out, int ctb, u32 len, int hdrlen ); static int write_new_header( IOBUF out, int ctb, u32 len, int hdrlen ); static int write_version( IOBUF out, int ctb ); /**************** * Build a packet and write it to INP * Returns: 0 := okay * >0 := error * Note: Caller must free the packet */ int build_packet( IOBUF out, PACKET *pkt ) { int new_ctb=0, rc=0, ctb; int pkttype; if( DBG_PACKET ) log_debug("build_packet() type=%d\n", pkt->pkttype ); assert( pkt->pkt.generic ); switch( (pkttype = pkt->pkttype) ) { case PKT_PLAINTEXT: new_ctb = pkt->pkt.plaintext->new_ctb; break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: new_ctb = pkt->pkt.encrypted->new_ctb; break; case PKT_COMPRESSED:new_ctb = pkt->pkt.compressed->new_ctb; break; case PKT_USER_ID: if( pkt->pkt.user_id->attrib_data ) pkttype = PKT_ATTRIBUTE; break; default: break; } if( new_ctb || pkttype > 15 ) /* new format */ ctb = 0xc0 | (pkttype & 0x3f); else ctb = 0x80 | ((pkttype & 15)<<2); switch( pkttype ) { case PKT_ATTRIBUTE: case PKT_USER_ID: rc = do_user_id( out, ctb, pkt->pkt.user_id ); break; case PKT_OLD_COMMENT: case PKT_COMMENT: /* Ignore these. Theoretically, this will never be called as we have no way to output comment packets any longer, but just in case there is some code path that would end up outputting a comment that was written before comments were dropped (in the public key?) this is a no-op. */ break; case PKT_PUBLIC_SUBKEY: case PKT_PUBLIC_KEY: rc = do_public_key( out, ctb, pkt->pkt.public_key ); break; case PKT_SECRET_SUBKEY: case PKT_SECRET_KEY: rc = do_secret_key( out, ctb, pkt->pkt.secret_key ); break; case PKT_SYMKEY_ENC: rc = do_symkey_enc( out, ctb, pkt->pkt.symkey_enc ); break; case PKT_PUBKEY_ENC: rc = do_pubkey_enc( out, ctb, pkt->pkt.pubkey_enc ); break; case PKT_PLAINTEXT: rc = do_plaintext( out, ctb, pkt->pkt.plaintext ); break; case PKT_ENCRYPTED: rc = do_encrypted( out, ctb, pkt->pkt.encrypted ); break; case PKT_ENCRYPTED_MDC: rc = do_encrypted_mdc( out, ctb, pkt->pkt.encrypted ); break; case PKT_COMPRESSED: rc = do_compressed( out, ctb, pkt->pkt.compressed ); break; case PKT_SIGNATURE: rc = do_signature( out, ctb, pkt->pkt.signature ); break; case PKT_ONEPASS_SIG: rc = do_onepass_sig( out, ctb, pkt->pkt.onepass_sig ); break; case PKT_RING_TRUST: break; /* ignore it (keyring.c does write it directly)*/ case PKT_MDC: /* we write it directly, so we should never see it here. */ default: log_bug("invalid packet type in build_packet()\n"); break; } return rc; } /**************** * calculate the length of a packet described by PKT */ u32 calc_packet_length( PACKET *pkt ) { u32 n=0; int new_ctb = 0; assert( pkt->pkt.generic ); switch( pkt->pkttype ) { case PKT_PLAINTEXT: n = calc_plaintext( pkt->pkt.plaintext ); new_ctb = pkt->pkt.plaintext->new_ctb; break; case PKT_ATTRIBUTE: case PKT_USER_ID: case PKT_COMMENT: case PKT_PUBLIC_KEY: case PKT_SECRET_KEY: case PKT_SYMKEY_ENC: case PKT_PUBKEY_ENC: case PKT_ENCRYPTED: case PKT_SIGNATURE: case PKT_ONEPASS_SIG: case PKT_RING_TRUST: case PKT_COMPRESSED: default: log_bug("invalid packet type in calc_packet_length()"); break; } n += calc_header_length(n, new_ctb); return n; } static void write_fake_data( IOBUF out, MPI a ) { if( a ) { unsigned int i; void *p; p = mpi_get_opaque( a, &i ); if (p) iobuf_write( out, p, i ); } } static int do_user_id( IOBUF out, int ctb, PKT_user_id *uid ) { if( uid->attrib_data ) { write_header(out, ctb, uid->attrib_len); if( iobuf_write( out, uid->attrib_data, uid->attrib_len ) ) return G10ERR_WRITE_FILE; } else { write_header2( out, ctb, uid->len, 2 ); if( iobuf_write( out, uid->name, uid->len ) ) return G10ERR_WRITE_FILE; } return 0; } static int do_public_key( IOBUF out, int ctb, PKT_public_key *pk ) { int rc = 0; int n, i; IOBUF a = iobuf_temp(); if( !pk->version ) iobuf_put( a, 3 ); else iobuf_put( a, pk->version ); write_32(a, pk->timestamp ); if( pk->version < 4 ) { u16 ndays; if( pk->expiredate ) ndays = (u16)((pk->expiredate - pk->timestamp) / 86400L); else ndays = 0; write_16(a, ndays ); } iobuf_put(a, pk->pubkey_algo ); n = pubkey_get_npkey( pk->pubkey_algo ); if( !n ) write_fake_data( a, pk->pkey[0] ); for(i=0; i < n; i++ ) mpi_write(a, pk->pkey[i] ); write_header2(out, ctb, iobuf_get_temp_length(a), pk->hdrbytes); if( iobuf_write_temp( out, a ) ) rc = G10ERR_WRITE_FILE; iobuf_close(a); return rc; } static int do_secret_key( IOBUF out, int ctb, PKT_secret_key *sk ) { int rc = 0; int i, nskey, npkey; IOBUF a = iobuf_temp(); /* build in a self-enlarging buffer */ /* Write the version number - if none is specified, use 3 */ if( !sk->version ) iobuf_put( a, 3 ); else iobuf_put( a, sk->version ); write_32(a, sk->timestamp ); /* v3 needs the expiration time */ if( sk->version < 4 ) { u16 ndays; if( sk->expiredate ) ndays = (u16)((sk->expiredate - sk->timestamp) / 86400L); else ndays = 0; write_16(a, ndays); } iobuf_put(a, sk->pubkey_algo ); /* get number of secret and public parameters. They are held in one array first the public ones, then the secret ones */ nskey = pubkey_get_nskey( sk->pubkey_algo ); npkey = pubkey_get_npkey( sk->pubkey_algo ); /* If we don't have any public parameters - which is the case if we don't know the algorithm used - the parameters are stored as one blob in a faked (opaque) MPI */ if( !npkey ) { write_fake_data( a, sk->skey[0] ); goto leave; } assert( npkey < nskey ); /* Writing the public parameters is easy */ for(i=0; i < npkey; i++ ) mpi_write(a, sk->skey[i] ); /* build the header for protected (encrypted) secret parameters */ if( sk->is_protected ) { if( is_RSA(sk->pubkey_algo) && sk->version < 4 && !sk->protect.s2k.mode ) { /* the simple rfc1991 (v3) way */ iobuf_put(a, sk->protect.algo ); iobuf_write(a, sk->protect.iv, sk->protect.ivlen ); } else { /* OpenPGP protection according to rfc2440 */ iobuf_put(a, sk->protect.sha1chk? 0xfe : 0xff ); iobuf_put(a, sk->protect.algo ); if( sk->protect.s2k.mode >= 1000 ) { /* These modes are not possible in OpenPGP, we use them to implement our extensions, 101 can be seen as a private/experimental extension (this is not specified in rfc2440 but the same scheme is used for all other algorithm identifiers) */ iobuf_put(a, 101 ); iobuf_put(a, sk->protect.s2k.hash_algo ); iobuf_write(a, "GNU", 3 ); iobuf_put(a, sk->protect.s2k.mode - 1000 ); } else { iobuf_put(a, sk->protect.s2k.mode ); iobuf_put(a, sk->protect.s2k.hash_algo ); } if( sk->protect.s2k.mode == 1 || sk->protect.s2k.mode == 3 ) iobuf_write(a, sk->protect.s2k.salt, 8 ); if( sk->protect.s2k.mode == 3 ) iobuf_put(a, sk->protect.s2k.count ); /* For out special modes 1001, 1002 we do not need an IV */ if( sk->protect.s2k.mode != 1001 && sk->protect.s2k.mode != 1002 ) iobuf_write(a, sk->protect.iv, sk->protect.ivlen ); } } else iobuf_put(a, 0 ); if( sk->protect.s2k.mode == 1001 ) ; /* GnuPG extension - don't write a secret key at all */ else if( sk->protect.s2k.mode == 1002 ) { /* GnuPG extension - divert to OpenPGP smartcard. */ iobuf_put(a, sk->protect.ivlen ); /* length of the serial number or 0 for no serial number. */ /* The serial number gets stored in the IV field. */ iobuf_write(a, sk->protect.iv, sk->protect.ivlen); } else if( sk->is_protected && sk->version >= 4 ) { /* The secret key is protected - write it out as it is */ byte *p; unsigned int ndata; assert( mpi_is_opaque( sk->skey[npkey] ) ); p = mpi_get_opaque( sk->skey[npkey], &ndata ); iobuf_write(a, p, ndata ); } else if( sk->is_protected ) { /* The secret key is protected te old v4 way. */ for( ; i < nskey; i++ ) { byte *p; unsigned int ndata; assert (mpi_is_opaque (sk->skey[i])); p = mpi_get_opaque (sk->skey[i], &ndata); iobuf_write (a, p, ndata); } write_16(a, sk->csum ); } else { /* non-protected key */ for( ; i < nskey; i++ ) mpi_write(a, sk->skey[i] ); write_16(a, sk->csum ); } leave: /* Build the header of the packet - which we must do after writing all the other stuff, so that we know the length of the packet */ write_header2(out, ctb, iobuf_get_temp_length(a), sk->hdrbytes); /* And finally write it out the real stream */ if( iobuf_write_temp( out, a ) ) rc = G10ERR_WRITE_FILE; iobuf_close(a); /* close the remporary buffer */ return rc; } static int do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ) { int rc = 0; IOBUF a = iobuf_temp(); assert( enc->version == 4 ); switch( enc->s2k.mode ) { case 0: case 1: case 3: break; default: log_bug("do_symkey_enc: s2k=%d\n", enc->s2k.mode ); } iobuf_put( a, enc->version ); iobuf_put( a, enc->cipher_algo ); iobuf_put( a, enc->s2k.mode ); iobuf_put( a, enc->s2k.hash_algo ); if( enc->s2k.mode == 1 || enc->s2k.mode == 3 ) { iobuf_write(a, enc->s2k.salt, 8 ); if( enc->s2k.mode == 3 ) iobuf_put(a, enc->s2k.count); } if( enc->seskeylen ) iobuf_write(a, enc->seskey, enc->seskeylen ); write_header(out, ctb, iobuf_get_temp_length(a) ); if( iobuf_write_temp( out, a ) ) rc = G10ERR_WRITE_FILE; iobuf_close(a); return rc; } static int do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ) { int rc = 0; int n, i; IOBUF a = iobuf_temp(); write_version( a, ctb ); if( enc->throw_keyid ) { write_32(a, 0 ); /* don't tell Eve who can decrypt the message */ write_32(a, 0 ); } else { write_32(a, enc->keyid[0] ); write_32(a, enc->keyid[1] ); } iobuf_put(a,enc->pubkey_algo ); n = pubkey_get_nenc( enc->pubkey_algo ); if( !n ) write_fake_data( a, enc->data[0] ); for(i=0; i < n; i++ ) mpi_write(a, enc->data[i] ); write_header(out, ctb, iobuf_get_temp_length(a) ); if( iobuf_write_temp( out, a ) ) rc = G10ERR_WRITE_FILE; iobuf_close(a); return rc; } static u32 calc_plaintext( PKT_plaintext *pt ) { /* Truncate namelen to the maximum 255 characters. Note this means that a function that calls build_packet with an illegal literal packet will get it back legalized. */ if(pt->namelen>255) pt->namelen=255; return pt->len? (1 + 1 + pt->namelen + 4 + pt->len) : 0; } static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt ) { int i, rc = 0; u32 n; byte buf[1000]; /* this buffer has the plaintext! */ int nbytes; write_header(out, ctb, calc_plaintext( pt ) ); iobuf_put(out, pt->mode ); iobuf_put(out, pt->namelen ); for(i=0; i < pt->namelen; i++ ) iobuf_put(out, pt->name[i] ); if( write_32(out, pt->timestamp ) ) rc = G10ERR_WRITE_FILE; n = 0; while( (nbytes=iobuf_read(pt->buf, buf, 1000)) != -1 ) { if( iobuf_write(out, buf, nbytes) == -1 ) { rc = G10ERR_WRITE_FILE; break; } n += nbytes; } wipememory(buf,1000); /* burn the buffer */ if( (ctb&0x40) && !pt->len ) iobuf_set_partial_block_mode(out, 0 ); /* turn off partial */ /* On VMS, byte counts will not match for some file record * formats, so it's best to disable the following error. */ #ifndef __VMS if( pt->len && n != pt->len ) log_error("do_plaintext(): wrote %lu bytes but expected %lu bytes\n", (ulong)n, (ulong)pt->len ); #endif return rc; } static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed ) { int rc = 0; u32 n; n = ed->len ? (ed->len + ed->extralen) : 0; write_header(out, ctb, n ); /* This is all. The caller has to write the real data */ return rc; } static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ) { int rc = 0; u32 n; assert( ed->mdc_method ); /* Take version number and the following MDC packet in account. */ n = ed->len ? (ed->len + ed->extralen + 1 + 22) : 0; write_header(out, ctb, n ); iobuf_put(out, 1 ); /* version */ /* This is all. The caller has to write the real data */ return rc; } static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd ) { int rc = 0; /* We must use the old convention and don't use blockmode for tyhe sake of PGP 2 compatibility. However if the new_ctb flag was set, CTB is already formatted as new style and write_header2 does create a partial length encoding using new the new style. */ write_header2(out, ctb, 0, 0); iobuf_put(out, cd->algorithm ); /* This is all. The caller has to write the real data */ return rc; } /**************** * Delete all subpackets of type REQTYPE and return a bool whether a packet * was deleted. */ int delete_sig_subpkt (subpktarea_t *area, sigsubpkttype_t reqtype ) { int buflen; sigsubpkttype_t type; byte *buffer, *bufstart; size_t n; size_t unused = 0; int okay = 0; if( !area ) return 0; buflen = area->len; buffer = area->data; for(;;) { if( !buflen ) { okay = 1; break; } bufstart = buffer; n = *buffer++; buflen--; if( n == 255 ) { if( buflen < 4 ) break; - n = (buffer[0] << 24) | (buffer[1] << 16) - | (buffer[2] << 8) | buffer[3]; + n = buf32_to_size_t (buffer); buffer += 4; buflen -= 4; } else if( n >= 192 ) { if( buflen < 2 ) break; n = (( n - 192 ) << 8) + *buffer + 192; buffer++; buflen--; } if( buflen < n ) break; type = *buffer & 0x7f; if( type == reqtype ) { buffer++; buflen--; n--; if( n > buflen ) break; buffer += n; /* point to next subpkt */ buflen -= n; memmove (bufstart, buffer, buflen); /* shift */ unused += buffer - bufstart; buffer = bufstart; } else { buffer += n; buflen -=n; } } if (!okay) log_error ("delete_subpkt: buffer shorter than subpacket\n"); assert (unused <= area->len); area->len -= unused; return !!unused; } /**************** * Create or update a signature subpacket for SIG of TYPE. This * functions knows where to put the data (hashed or unhashed). The * function may move data from the unhashed part to the hashed one. * Note: All pointers into sig->[un]hashed (e.g. returned by * parse_sig_subpkt) are not valid after a call to this function. The * data to put into the subpaket should be in a buffer with a length * of buflen. */ void build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type, const byte *buffer, size_t buflen ) { byte *p; int critical, hashed; subpktarea_t *oldarea, *newarea; size_t nlen, n, n0; critical = (type & SIGSUBPKT_FLAG_CRITICAL); type &= ~SIGSUBPKT_FLAG_CRITICAL; /* Sanity check buffer sizes */ if(parse_one_sig_subpkt(buffer,buflen,type)<0) BUG(); switch(type) { case SIGSUBPKT_NOTATION: case SIGSUBPKT_POLICY: case SIGSUBPKT_REV_KEY: case SIGSUBPKT_SIGNATURE: /* we do allow multiple subpackets */ break; default: /* we don't allow multiple subpackets */ delete_sig_subpkt(sig->hashed,type); delete_sig_subpkt(sig->unhashed,type); break; } /* Any special magic that needs to be done for this type so the packet doesn't need to be reparsed? */ switch(type) { case SIGSUBPKT_NOTATION: sig->flags.notation=1; break; case SIGSUBPKT_POLICY: sig->flags.policy_url=1; break; case SIGSUBPKT_PREF_KS: sig->flags.pref_ks=1; break; case SIGSUBPKT_EXPORTABLE: if(buffer[0]) sig->flags.exportable=1; else sig->flags.exportable=0; break; case SIGSUBPKT_REVOCABLE: if(buffer[0]) sig->flags.revocable=1; else sig->flags.revocable=0; break; case SIGSUBPKT_TRUST: sig->trust_depth=buffer[0]; sig->trust_value=buffer[1]; break; case SIGSUBPKT_REGEXP: sig->trust_regexp=buffer; break; /* This should never happen since we don't currently allow creating such a subpacket, but just in case... */ case SIGSUBPKT_SIG_EXPIRE: - if(buffer_to_u32(buffer)+sig->timestamp<=make_timestamp()) + if (buf32_to_u32 (buffer) + sig->timestamp <= make_timestamp()) sig->flags.expired=1; else sig->flags.expired=0; break; default: break; } if( (buflen+1) >= 8384 ) nlen = 5; /* write 5 byte length header */ else if( (buflen+1) >= 192 ) nlen = 2; /* write 2 byte length header */ else nlen = 1; /* just a 1 byte length header */ switch( type ) { /* The issuer being unhashed is a historical oddity. It should work equally as well hashed. Of course, if even an unhashed issuer is tampered with, it makes it awfully hard to verify the sig... */ case SIGSUBPKT_ISSUER: case SIGSUBPKT_SIGNATURE: hashed = 0; break; default: hashed = 1; break; } if( critical ) type |= SIGSUBPKT_FLAG_CRITICAL; oldarea = hashed? sig->hashed : sig->unhashed; /* Calculate new size of the area and allocate */ n0 = oldarea? oldarea->len : 0; n = n0 + nlen + 1 + buflen; /* length, type, buffer */ if (oldarea && n <= oldarea->size) { /* fits into the unused space */ newarea = oldarea; /*log_debug ("updating area for type %d\n", type );*/ } else if (oldarea) { newarea = xrealloc (oldarea, sizeof (*newarea) + n - 1); newarea->size = n; /*log_debug ("reallocating area for type %d\n", type );*/ } else { newarea = xmalloc (sizeof (*newarea) + n - 1); newarea->size = n; /*log_debug ("allocating area for type %d\n", type );*/ } newarea->len = n; p = newarea->data + n0; if (nlen == 5) { *p++ = 255; *p++ = (buflen+1) >> 24; *p++ = (buflen+1) >> 16; *p++ = (buflen+1) >> 8; *p++ = (buflen+1); *p++ = type; memcpy (p, buffer, buflen); } else if (nlen == 2) { *p++ = (buflen+1-192) / 256 + 192; *p++ = (buflen+1-192) % 256; *p++ = type; memcpy (p, buffer, buflen); } else { *p++ = buflen+1; *p++ = type; memcpy (p, buffer, buflen); } if (hashed) sig->hashed = newarea; else sig->unhashed = newarea; } /**************** * Put all the required stuff from SIG into subpackets of sig. * Hmmm, should we delete those subpackets which are in a wrong area? */ void build_sig_subpkt_from_sig( PKT_signature *sig ) { u32 u; byte buf[8]; u = sig->keyid[0]; buf[0] = (u >> 24) & 0xff; buf[1] = (u >> 16) & 0xff; buf[2] = (u >> 8) & 0xff; buf[3] = u & 0xff; u = sig->keyid[1]; buf[4] = (u >> 24) & 0xff; buf[5] = (u >> 16) & 0xff; buf[6] = (u >> 8) & 0xff; buf[7] = u & 0xff; build_sig_subpkt( sig, SIGSUBPKT_ISSUER, buf, 8 ); u = sig->timestamp; buf[0] = (u >> 24) & 0xff; buf[1] = (u >> 16) & 0xff; buf[2] = (u >> 8) & 0xff; buf[3] = u & 0xff; build_sig_subpkt( sig, SIGSUBPKT_SIG_CREATED, buf, 4 ); if(sig->expiredate) { if(sig->expiredate>sig->timestamp) u=sig->expiredate-sig->timestamp; else u=1; /* A 1-second expiration time is the shortest one OpenPGP has */ buf[0] = (u >> 24) & 0xff; buf[1] = (u >> 16) & 0xff; buf[2] = (u >> 8) & 0xff; buf[3] = u & 0xff; /* Mark this CRITICAL, so if any implementation doesn't understand sigs that can expire, it'll just disregard this sig altogether. */ build_sig_subpkt( sig, SIGSUBPKT_SIG_EXPIRE | SIGSUBPKT_FLAG_CRITICAL, buf, 4 ); } } void build_attribute_subpkt(PKT_user_id *uid,byte type, const void *buf,u32 buflen, const void *header,u32 headerlen) { byte *attrib; int idx; if(1+headerlen+buflen>8383) idx=5; else if(1+headerlen+buflen>191) idx=2; else idx=1; /* realloc uid->attrib_data to the right size */ uid->attrib_data=xrealloc(uid->attrib_data, uid->attrib_len+idx+1+headerlen+buflen); attrib=&uid->attrib_data[uid->attrib_len]; if(idx==5) { attrib[0]=255; attrib[1]=(1+headerlen+buflen) >> 24; attrib[2]=(1+headerlen+buflen) >> 16; attrib[3]=(1+headerlen+buflen) >> 8; attrib[4]=1+headerlen+buflen; } else if(idx==2) { attrib[0]=(1+headerlen+buflen-192) / 256 + 192; attrib[1]=(1+headerlen+buflen-192) % 256; } else attrib[0]=1+headerlen+buflen; /* Good luck finding a JPEG this small! */ attrib[idx++]=type; /* Tack on our data at the end */ if(headerlen>0) memcpy(&attrib[idx],header,headerlen); memcpy(&attrib[idx+headerlen],buf,buflen); uid->attrib_len+=idx+headerlen+buflen; } struct notation * string_to_notation(const char *string,int is_utf8) { const char *s; int saw_at=0; struct notation *notation; notation=xmalloc_clear(sizeof(*notation)); if(*string=='-') { notation->flags.ignore=1; string++; } if(*string=='!') { notation->flags.critical=1; string++; } /* If and when the IETF assigns some official name tags, we'll have to add them here. */ for( s=string ; *s != '='; s++ ) { if( *s=='@') saw_at++; /* -notationname is legal without an = sign */ if(!*s && notation->flags.ignore) break; if( !*s || !isascii (*s) || (!isgraph(*s) && !isspace(*s)) ) { log_error(_("a notation name must have only printable characters" " or spaces, and end with an '='\n") ); goto fail; } } notation->name=xmalloc((s-string)+1); strncpy(notation->name,string,s-string); notation->name[s-string]='\0'; if(!saw_at && !opt.expert) { log_error(_("a user notation name must contain the '@' character\n")); goto fail; } if (saw_at > 1) { log_error(_("a notation name must not contain more than" " one '@' character\n")); goto fail; } if(*s) { const char *i=s+1; int highbit=0; /* we only support printable text - therefore we enforce the use of only printable characters (an empty value is valid) */ for(s++; *s ; s++ ) { if ( !isascii (*s) ) highbit=1; else if (iscntrl(*s)) { log_error(_("a notation value must not use any" " control characters\n")); goto fail; } } if(!highbit || is_utf8) notation->value=xstrdup(i); else notation->value=native_to_utf8(i); } return notation; fail: free_notation(notation); return NULL; } struct notation * sig_to_notation(PKT_signature *sig) { const byte *p; size_t len; int seq=0,crit; struct notation *list=NULL; while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,&len,&seq,&crit))) { int n1,n2; struct notation *n=NULL; if(len<8) { log_info(_("WARNING: invalid notation data found\n")); continue; } n1=(p[4]<<8)|p[5]; n2=(p[6]<<8)|p[7]; if(8+n1+n2!=len) { log_info(_("WARNING: invalid notation data found\n")); continue; } n=xmalloc_clear(sizeof(*n)); n->name=xmalloc(n1+1); memcpy(n->name,&p[8],n1); n->name[n1]='\0'; if(p[0]&0x80) { n->value=xmalloc(n2+1); memcpy(n->value,&p[8+n1],n2); n->value[n2]='\0'; } else { n->bdat=xmalloc(n2); n->blen=n2; memcpy(n->bdat,&p[8+n1],n2); n->value=xmalloc(2+strlen(_("not human readable"))+2+1); strcpy(n->value,"[ "); strcat(n->value,_("not human readable")); strcat(n->value," ]"); } n->flags.critical=crit; n->next=list; list=n; } return list; } void free_notation(struct notation *notation) { while(notation) { struct notation *n=notation; xfree(n->name); xfree(n->value); xfree(n->altvalue); xfree(n->bdat); notation=n->next; xfree(n); } } static int do_signature( IOBUF out, int ctb, PKT_signature *sig ) { int rc = 0; int n, i; IOBUF a = iobuf_temp(); if( !sig->version ) iobuf_put( a, 3 ); else iobuf_put( a, sig->version ); if( sig->version < 4 ) iobuf_put(a, 5 ); /* constant */ iobuf_put(a, sig->sig_class ); if( sig->version < 4 ) { write_32(a, sig->timestamp ); write_32(a, sig->keyid[0] ); write_32(a, sig->keyid[1] ); } iobuf_put(a, sig->pubkey_algo ); iobuf_put(a, sig->digest_algo ); if( sig->version >= 4 ) { size_t nn; /* timestamp and keyid must have been packed into the * subpackets prior to the call of this function, because * these subpackets are hashed */ nn = sig->hashed? sig->hashed->len : 0; write_16(a, nn); if( nn ) iobuf_write( a, sig->hashed->data, nn ); nn = sig->unhashed? sig->unhashed->len : 0; write_16(a, nn); if( nn ) iobuf_write( a, sig->unhashed->data, nn ); } iobuf_put(a, sig->digest_start[0] ); iobuf_put(a, sig->digest_start[1] ); n = pubkey_get_nsig( sig->pubkey_algo ); if( !n ) write_fake_data( a, sig->data[0] ); for(i=0; i < n; i++ ) mpi_write(a, sig->data[i] ); if( is_RSA(sig->pubkey_algo) && sig->version < 4 ) write_sign_packet_header(out, ctb, iobuf_get_temp_length(a) ); else write_header(out, ctb, iobuf_get_temp_length(a) ); if( iobuf_write_temp( out, a ) ) rc = G10ERR_WRITE_FILE; iobuf_close(a); return rc; } static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops ) { int rc = 0; IOBUF a = iobuf_temp(); write_version( a, ctb ); iobuf_put(a, ops->sig_class ); iobuf_put(a, ops->digest_algo ); iobuf_put(a, ops->pubkey_algo ); write_32(a, ops->keyid[0] ); write_32(a, ops->keyid[1] ); iobuf_put(a, ops->last ); write_header(out, ctb, iobuf_get_temp_length(a) ); if( iobuf_write_temp( out, a ) ) rc = G10ERR_WRITE_FILE; iobuf_close(a); return rc; } static int write_16(IOBUF out, u16 a) { iobuf_put(out, a>>8); if( iobuf_put(out,a) ) return -1; return 0; } static int write_32(IOBUF out, u32 a) { iobuf_put(out, a>> 24); iobuf_put(out, a>> 16); iobuf_put(out, a>> 8); if( iobuf_put(out, a) ) return -1; return 0; } /**************** * calculate the length of a header */ static int calc_header_length( u32 len, int new_ctb ) { if( !len ) return 1; /* only the ctb */ if( new_ctb ) { if( len < 192 ) return 2; if( len < 8384 ) return 3; else return 6; } if( len < 256 ) return 2; if( len < 65536 ) return 3; return 5; } /**************** * Write the CTB and the packet length */ static int write_header( IOBUF out, int ctb, u32 len ) { return write_header2( out, ctb, len, 0 ); } static int write_sign_packet_header( IOBUF out, int ctb, u32 len ) { (void)ctb; /* work around a bug in the pgp read function for signature packets, * which are not correctly coded and silently assume at some * point 2 byte length headers.*/ iobuf_put(out, 0x89 ); iobuf_put(out, len >> 8 ); return iobuf_put(out, len ) == -1 ? -1:0; } /**************** * If HDRLEN is > 0, try to build a header of this length. We need * this so that we can hash packets without reading them again. If * len is 0, write a partial or indeterminate length header, unless * hdrlen is specified in which case write an actual zero length * (using the specified hdrlen). */ static int write_header2( IOBUF out, int ctb, u32 len, int hdrlen ) { if( ctb & 0x40 ) return write_new_header( out, ctb, len, hdrlen ); if( hdrlen ) { if( hdrlen == 2 && len < 256 ) ; else if( hdrlen == 3 && len < 65536 ) ctb |= 1; else ctb |= 2; } else { if( !len ) ctb |= 3; else if( len < 256 ) ; else if( len < 65536 ) ctb |= 1; else ctb |= 2; } if( iobuf_put(out, ctb ) ) return -1; if( len || hdrlen ) { if( ctb & 2 ) { if(iobuf_put(out, len >> 24 )) return -1; if(iobuf_put(out, len >> 16 )) return -1; } if( ctb & 3 ) if(iobuf_put(out, len >> 8 )) return -1; if( iobuf_put(out, len ) ) return -1; } return 0; } static int write_new_header( IOBUF out, int ctb, u32 len, int hdrlen ) { if( hdrlen ) log_bug("can't cope with hdrlen yet\n"); if( iobuf_put(out, ctb ) ) return -1; if( !len ) { iobuf_set_partial_block_mode(out, 512 ); } else { if( len < 192 ) { if( iobuf_put(out, len ) ) return -1; } else if( len < 8384 ) { len -= 192; if( iobuf_put( out, (len / 256) + 192) ) return -1; if( iobuf_put( out, (len % 256) ) ) return -1; } else { if( iobuf_put( out, 0xff ) ) return -1; if( iobuf_put( out, (len >> 24)&0xff ) ) return -1; if( iobuf_put( out, (len >> 16)&0xff ) ) return -1; if( iobuf_put( out, (len >> 8)&0xff ) ) return -1; if( iobuf_put( out, len & 0xff ) ) return -1; } } return 0; } static int write_version( IOBUF out, int ctb ) { (void)ctb; if( iobuf_put( out, 3 ) ) return -1; return 0; } diff --git a/g10/ccid-driver.c b/g10/ccid-driver.c index 8c362d73c..515b15a6b 100644 --- a/g10/ccid-driver.c +++ b/g10/ccid-driver.c @@ -1,3490 +1,3491 @@ /* ccid-driver.c - USB ChipCardInterfaceDevices driver * Copyright (C) 2003, 2004, 2005, 2006, 2007 * 2008, 2009 Free Software Foundation, Inc. * Written by Werner Koch. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * ALTERNATIVELY, this file may be distributed under the terms of the * following license, in which case the provisions of this license are * required INSTEAD OF the GNU General Public License. If you wish to * allow use of your version of this file only under the terms of the * GNU General Public License, and not to allow others to use your * version of this file under the terms of the following license, * indicate your decision by deleting this paragraph and the license * below. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * $Date$ */ /* CCID (ChipCardInterfaceDevices) is a specification for accessing smartcard via a reader connected to the USB. This is a limited driver allowing to use some CCID drivers directly without any other specila drivers. This is a fallback driver to be used when nothing else works or the system should be kept minimal for security reasons. It makes use of the libusb library to gain portable access to USB. This driver has been tested with the SCM SCR335 and SPR532 smartcard readers and requires that a reader implements APDU or TPDU level exchange and does fully automatic initialization. */ #ifdef HAVE_CONFIG_H # include #endif #if defined(HAVE_LIBUSB) || defined(TEST) #include #include #include #include #include #include #include #include #include #ifdef HAVE_PTH # include #endif /*HAVE_PTH*/ #include #include "ccid-driver.h" +#include "../include/host2net.h" #define DRVNAME "ccid-driver: " /* Depending on how this source is used we either define our error output to go to stderr or to the jnlib based logging functions. We use the latter when GNUPG_MAJOR_VERSION is defines or when both, GNUPG_SCD_MAIN_HEADER and HAVE_JNLIB_LOGGING are defined. */ #if defined(GNUPG_MAJOR_VERSION) \ || (defined(GNUPG_SCD_MAIN_HEADER) && defined(HAVE_JNLIB_LOGGING)) #if defined(GNUPG_SCD_MAIN_HEADER) # include GNUPG_SCD_MAIN_HEADER #elif GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */ # include "options.h" # include "util.h" # include "memory.h" # include "cardglue.h" # else /* This is the modularized GnuPG 1.9 or later. */ # include "scdaemon.h" #endif # define DEBUGOUT(t) do { if (debug_level) \ log_debug (DRVNAME t); } while (0) # define DEBUGOUT_1(t,a) do { if (debug_level) \ log_debug (DRVNAME t,(a)); } while (0) # define DEBUGOUT_2(t,a,b) do { if (debug_level) \ log_debug (DRVNAME t,(a),(b)); } while (0) # define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \ log_debug (DRVNAME t,(a),(b),(c));} while (0) # define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \ log_debug (DRVNAME t,(a),(b),(c),(d));} while (0) # define DEBUGOUT_CONT(t) do { if (debug_level) \ log_printf (t); } while (0) # define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \ log_printf (t,(a)); } while (0) # define DEBUGOUT_CONT_2(t,a,b) do { if (debug_level) \ log_printf (t,(a),(b)); } while (0) # define DEBUGOUT_CONT_3(t,a,b,c) do { if (debug_level) \ log_printf (t,(a),(b),(c)); } while (0) # define DEBUGOUT_LF() do { if (debug_level) \ log_printf ("\n"); } while (0) #else /* Other usage of this source - don't use gnupg specifics. */ # define DEBUGOUT(t) do { if (debug_level) \ fprintf (stderr, DRVNAME t); } while (0) # define DEBUGOUT_1(t,a) do { if (debug_level) \ fprintf (stderr, DRVNAME t, (a)); } while (0) # define DEBUGOUT_2(t,a,b) do { if (debug_level) \ fprintf (stderr, DRVNAME t, (a), (b)); } while (0) # define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \ fprintf (stderr, DRVNAME t, (a), (b), (c)); } while (0) # define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \ fprintf (stderr, DRVNAME t, (a), (b), (c), (d));} while(0) # define DEBUGOUT_CONT(t) do { if (debug_level) \ fprintf (stderr, t); } while (0) # define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \ fprintf (stderr, t, (a)); } while (0) # define DEBUGOUT_CONT_2(t,a,b) do { if (debug_level) \ fprintf (stderr, t, (a), (b)); } while (0) # define DEBUGOUT_CONT_3(t,a,b,c) do { if (debug_level) \ fprintf (stderr, t, (a), (b), (c)); } while (0) # define DEBUGOUT_LF() do { if (debug_level) \ putc ('\n', stderr); } while (0) #endif /* This source not used by scdaemon. */ #ifndef EAGAIN #define EAGAIN EWOULDBLOCK #endif enum { RDR_to_PC_NotifySlotChange= 0x50, RDR_to_PC_HardwareError = 0x51, PC_to_RDR_SetParameters = 0x61, PC_to_RDR_IccPowerOn = 0x62, PC_to_RDR_IccPowerOff = 0x63, PC_to_RDR_GetSlotStatus = 0x65, PC_to_RDR_Secure = 0x69, PC_to_RDR_T0APDU = 0x6a, PC_to_RDR_Escape = 0x6b, PC_to_RDR_GetParameters = 0x6c, PC_to_RDR_ResetParameters = 0x6d, PC_to_RDR_IccClock = 0x6e, PC_to_RDR_XfrBlock = 0x6f, PC_to_RDR_Mechanical = 0x71, PC_to_RDR_Abort = 0x72, PC_to_RDR_SetDataRate = 0x73, RDR_to_PC_DataBlock = 0x80, RDR_to_PC_SlotStatus = 0x81, RDR_to_PC_Parameters = 0x82, RDR_to_PC_Escape = 0x83, RDR_to_PC_DataRate = 0x84 }; /* Two macro to detect whether a CCID command has failed and to get the error code. These macros assume that we can access the mandatory first 10 bytes of a CCID message in BUF. */ #define CCID_COMMAND_FAILED(buf) ((buf)[7] & 0x40) #define CCID_ERROR_CODE(buf) (((unsigned char *)(buf))[8]) /* We need to know the vendor to do some hacks. */ enum { VENDOR_CHERRY = 0x046a, VENDOR_SCM = 0x04e6, VENDOR_OMNIKEY= 0x076b, VENDOR_GEMPC = 0x08e6, VENDOR_KAAN = 0x0d46 }; /* A list and a table with special transport descriptions. */ enum { TRANSPORT_USB = 0, /* Standard USB transport. */ TRANSPORT_CM4040 = 1 /* As used by the Cardman 4040. */ }; static struct { char *name; /* Device name. */ int type; } transports[] = { { "/dev/cmx0", TRANSPORT_CM4040 }, { "/dev/cmx1", TRANSPORT_CM4040 }, { NULL }, }; /* Store information on the driver's state. A pointer to such a structure is used as handle for most functions. */ struct ccid_driver_s { usb_dev_handle *idev; char *rid; int dev_fd; /* -1 for USB transport or file descriptor of the transport device. */ unsigned short id_vendor; unsigned short id_product; unsigned short bcd_device; int ifc_no; int ep_bulk_out; int ep_bulk_in; int ep_intr; int seqno; unsigned char t1_ns; unsigned char t1_nr; unsigned char nonnull_nad; int max_ifsd; int ifsd; int ifsc; unsigned char apdu_level:2; /* Reader supports short APDU level exchange. With a value of 2 short and extended level is supported.*/ unsigned int auto_ifsd:1; unsigned int powered_off:1; unsigned int has_pinpad:2; unsigned int enodev_seen:1; time_t last_progress; /* Last time we sent progress line. */ /* The progress callback and its first arg as supplied to ccid_set_progress_cb. */ void (*progress_cb)(void *, const char *, int, int, int); void *progress_cb_arg; }; static int initialized_usb; /* Tracks whether USB has been initialized. */ static int debug_level; /* Flag to control the debug output. 0 = No debugging 1 = USB I/O info 2 = Level 1 + T=1 protocol tracing 3 = Level 2 + USB/I/O tracing of SlotStatus. */ static unsigned int compute_edc (const unsigned char *data, size_t datalen, int use_crc); static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen, int no_debug); static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, size_t *nread, int expected_type, int seqno, int timeout, int no_debug); static int abort_cmd (ccid_driver_t handle, int seqno); /* Convert a little endian stored 4 byte value into an unsigned integer. */ static unsigned int convert_le_u32 (const unsigned char *buf) { - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | ((unsigned int)buf[3] << 24); } /* Convert a little endian stored 2 byte value into an unsigned integer. */ static unsigned int convert_le_u16 (const unsigned char *buf) { return buf[0] | (buf[1] << 8); } static void set_msg_len (unsigned char *msg, unsigned int length) { msg[1] = length; msg[2] = length >> 8; msg[3] = length >> 16; msg[4] = length >> 24; } static void my_sleep (int seconds) { #ifdef HAVE_PTH /* With Pth we also call the standard sleep(0) so that the process may give up its timeslot. */ if (!seconds) { # ifdef HAVE_W32_SYSTEM Sleep (0); # else sleep (0); # endif } pth_sleep (seconds); #else # ifdef HAVE_W32_SYSTEM Sleep (seconds*1000); # else sleep (seconds); # endif #endif } static void print_progress (ccid_driver_t handle) { time_t ct = time (NULL); /* We don't want to print progress lines too often. */ if (ct == handle->last_progress) return; if (handle->progress_cb) handle->progress_cb (handle->progress_cb_arg, "card_busy", 'w', 0, 0); handle->last_progress = ct; } /* Pint an error message for a failed CCID command including a textual error code. MSG shall be the CCID message at a minimum of 10 bytes. */ static void print_command_failed (const unsigned char *msg) { const char *t; char buffer[100]; int ec; if (!debug_level) return; ec = CCID_ERROR_CODE (msg); switch (ec) { case 0x00: t = "Command not supported"; break; case 0xE0: t = "Slot busy"; break; case 0xEF: t = "PIN cancelled"; break; case 0xF0: t = "PIN timeout"; break; case 0xF2: t = "Automatic sequence ongoing"; break; case 0xF3: t = "Deactivated Protocol"; break; case 0xF4: t = "Procedure byte conflict"; break; case 0xF5: t = "ICC class not supported"; break; case 0xF6: t = "ICC protocol not supported"; break; case 0xF7: t = "Bad checksum in ATR"; break; case 0xF8: t = "Bad TS in ATR"; break; case 0xFB: t = "An all inclusive hardware error occurred"; break; case 0xFC: t = "Overrun error while talking to the ICC"; break; case 0xFD: t = "Parity error while talking to the ICC"; break; case 0xFE: t = "CCID timed out while talking to the ICC"; break; case 0xFF: t = "Host aborted the current activity"; break; default: if (ec > 0 && ec < 128) sprintf (buffer, "Parameter error at offset %d", ec); else sprintf (buffer, "Error code %02X", ec); t = buffer; break; } DEBUGOUT_1 ("CCID command failed: %s\n", t); } static void print_pr_data (const unsigned char *data, size_t datalen, size_t off) { int any = 0; for (; off < datalen; off++) { if (!any || !(off % 16)) { if (any) DEBUGOUT_LF (); DEBUGOUT_1 (" [%04lu] ", (unsigned long) off); } DEBUGOUT_CONT_1 (" %02X", data[off]); any = 1; } if (any && (off % 16)) DEBUGOUT_LF (); } static void print_p2r_header (const char *name, const unsigned char *msg, size_t msglen) { DEBUGOUT_1 ("%s:\n", name); if (msglen < 7) return; DEBUGOUT_1 (" dwLength ..........: %u\n", convert_le_u32 (msg+1)); DEBUGOUT_1 (" bSlot .............: %u\n", msg[5]); DEBUGOUT_1 (" bSeq ..............: %u\n", msg[6]); } static void print_p2r_iccpoweron (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_IccPowerOn", msg, msglen); if (msglen < 10) return; DEBUGOUT_2 (" bPowerSelect ......: 0x%02x (%s)\n", msg[7], msg[7] == 0? "auto": msg[7] == 1? "5.0 V": msg[7] == 2? "3.0 V": msg[7] == 3? "1.8 V":""); print_pr_data (msg, msglen, 8); } static void print_p2r_iccpoweroff (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_IccPowerOff", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_getslotstatus (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_GetSlotStatus", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_xfrblock (const unsigned char *msg, size_t msglen) { unsigned int val; print_p2r_header ("PC_to_RDR_XfrBlock", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" bBWI ..............: 0x%02x\n", msg[7]); val = convert_le_u16 (msg+8); DEBUGOUT_2 (" wLevelParameter ...: 0x%04x%s\n", val, val == 1? " (continued)": val == 2? " (continues+ends)": val == 3? " (continues+continued)": val == 16? " (DataBlock-expected)":""); print_pr_data (msg, msglen, 10); } static void print_p2r_getparameters (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_GetParameters", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_resetparameters (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_ResetParameters", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_setparameters (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_SetParameters", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" bProtocolNum ......: 0x%02x\n", msg[7]); print_pr_data (msg, msglen, 8); } static void print_p2r_escape (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_Escape", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_iccclock (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_IccClock", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" bClockCommand .....: 0x%02x\n", msg[7]); print_pr_data (msg, msglen, 8); } static void print_p2r_to0apdu (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_T0APDU", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" bmChanges .........: 0x%02x\n", msg[7]); DEBUGOUT_1 (" bClassGetResponse .: 0x%02x\n", msg[8]); DEBUGOUT_1 (" bClassEnvelope ....: 0x%02x\n", msg[9]); print_pr_data (msg, msglen, 10); } static void print_p2r_secure (const unsigned char *msg, size_t msglen) { unsigned int val; print_p2r_header ("PC_to_RDR_Secure", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" bBMI ..............: 0x%02x\n", msg[7]); val = convert_le_u16 (msg+8); DEBUGOUT_2 (" wLevelParameter ...: 0x%04x%s\n", val, val == 1? " (continued)": val == 2? " (continues+ends)": val == 3? " (continues+continued)": val == 16? " (DataBlock-expected)":""); print_pr_data (msg, msglen, 10); } static void print_p2r_mechanical (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_Mechanical", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" bFunction .........: 0x%02x\n", msg[7]); print_pr_data (msg, msglen, 8); } static void print_p2r_abort (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_Abort", msg, msglen); print_pr_data (msg, msglen, 7); } static void print_p2r_setdatarate (const unsigned char *msg, size_t msglen) { print_p2r_header ("PC_to_RDR_SetDataRate", msg, msglen); if (msglen < 10) return; print_pr_data (msg, msglen, 7); } static void print_p2r_unknown (const unsigned char *msg, size_t msglen) { print_p2r_header ("Unknown PC_to_RDR command", msg, msglen); if (msglen < 10) return; print_pr_data (msg, msglen, 0); } static void print_r2p_header (const char *name, const unsigned char *msg, size_t msglen) { DEBUGOUT_1 ("%s:\n", name); if (msglen < 9) return; DEBUGOUT_1 (" dwLength ..........: %u\n", convert_le_u32 (msg+1)); DEBUGOUT_1 (" bSlot .............: %u\n", msg[5]); DEBUGOUT_1 (" bSeq ..............: %u\n", msg[6]); DEBUGOUT_1 (" bStatus ...........: %u\n", msg[7]); if (msg[8]) DEBUGOUT_1 (" bError ............: %u\n", msg[8]); } static void print_r2p_datablock (const unsigned char *msg, size_t msglen) { print_r2p_header ("RDR_to_PC_DataBlock", msg, msglen); if (msglen < 10) return; if (msg[9]) DEBUGOUT_2 (" bChainParameter ...: 0x%02x%s\n", msg[9], msg[9] == 1? " (continued)": msg[9] == 2? " (continues+ends)": msg[9] == 3? " (continues+continued)": msg[9] == 16? " (XferBlock-expected)":""); print_pr_data (msg, msglen, 10); } static void print_r2p_slotstatus (const unsigned char *msg, size_t msglen) { print_r2p_header ("RDR_to_PC_SlotStatus", msg, msglen); if (msglen < 10) return; DEBUGOUT_2 (" bClockStatus ......: 0x%02x%s\n", msg[9], msg[9] == 0? " (running)": msg[9] == 1? " (stopped-L)": msg[9] == 2? " (stopped-H)": msg[9] == 3? " (stopped)":""); print_pr_data (msg, msglen, 10); } static void print_r2p_parameters (const unsigned char *msg, size_t msglen) { print_r2p_header ("RDR_to_PC_Parameters", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" protocol ..........: T=%d\n", msg[9]); if (msglen == 17 && msg[9] == 1) { /* Protocol T=1. */ DEBUGOUT_1 (" bmFindexDindex ....: %02X\n", msg[10]); DEBUGOUT_1 (" bmTCCKST1 .........: %02X\n", msg[11]); DEBUGOUT_1 (" bGuardTimeT1 ......: %02X\n", msg[12]); DEBUGOUT_1 (" bmWaitingIntegersT1: %02X\n", msg[13]); DEBUGOUT_1 (" bClockStop ........: %02X\n", msg[14]); DEBUGOUT_1 (" bIFSC .............: %d\n", msg[15]); DEBUGOUT_1 (" bNadValue .........: %d\n", msg[16]); } else print_pr_data (msg, msglen, 10); } static void print_r2p_escape (const unsigned char *msg, size_t msglen) { print_r2p_header ("RDR_to_PC_Escape", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" buffer[9] .........: %02X\n", msg[9]); print_pr_data (msg, msglen, 10); } static void print_r2p_datarate (const unsigned char *msg, size_t msglen) { print_r2p_header ("RDR_to_PC_DataRate", msg, msglen); if (msglen < 10) return; if (msglen >= 18) { DEBUGOUT_1 (" dwClockFrequency ..: %u\n", convert_le_u32 (msg+10)); DEBUGOUT_1 (" dwDataRate ..... ..: %u\n", convert_le_u32 (msg+14)); print_pr_data (msg, msglen, 18); } else print_pr_data (msg, msglen, 10); } static void print_r2p_unknown (const unsigned char *msg, size_t msglen) { print_r2p_header ("Unknown RDR_to_PC command", msg, msglen); if (msglen < 10) return; DEBUGOUT_1 (" bMessageType ......: %02X\n", msg[0]); DEBUGOUT_1 (" buffer[9] .........: %02X\n", msg[9]); print_pr_data (msg, msglen, 10); } /* Given a handle used for special transport prepare it for use. In particular setup all information in way that resembles what parse_cccid_descriptor does. */ static void prepare_special_transport (ccid_driver_t handle) { assert (!handle->id_vendor); handle->nonnull_nad = 0; handle->auto_ifsd = 0; handle->max_ifsd = 32; handle->ifsd = 0; handle->has_pinpad = 0; handle->apdu_level = 0; switch (handle->id_product) { case TRANSPORT_CM4040: DEBUGOUT ("setting up transport for CardMan 4040\n"); handle->apdu_level = 1; break; default: assert (!"transport not defined"); } } /* Parse a CCID descriptor, optionally print all available features and test whether this reader is usable by this driver. Returns 0 if it is usable. Note, that this code is based on the one in lsusb.c of the usb-utils package, I wrote on 2003-09-01. -wk. */ static int parse_ccid_descriptor (ccid_driver_t handle, const unsigned char *buf, size_t buflen) { unsigned int i; unsigned int us; int have_t1 = 0, have_tpdu=0, have_auto_conf = 0; handle->nonnull_nad = 0; handle->auto_ifsd = 0; handle->max_ifsd = 32; handle->ifsd = 0; handle->has_pinpad = 0; handle->apdu_level = 0; DEBUGOUT_3 ("idVendor: %04X idProduct: %04X bcdDevice: %04X\n", handle->id_vendor, handle->id_product, handle->bcd_device); if (buflen < 54 || buf[0] < 54) { DEBUGOUT ("CCID device descriptor is too short\n"); return -1; } DEBUGOUT ("ChipCard Interface Descriptor:\n"); DEBUGOUT_1 (" bLength %5u\n", buf[0]); DEBUGOUT_1 (" bDescriptorType %5u\n", buf[1]); DEBUGOUT_2 (" bcdCCID %2x.%02x", buf[3], buf[2]); if (buf[3] != 1 || buf[2] != 0) DEBUGOUT_CONT(" (Warning: Only accurate for version 1.0)"); DEBUGOUT_LF (); DEBUGOUT_1 (" nMaxSlotIndex %5u\n", buf[4]); DEBUGOUT_2 (" bVoltageSupport %5u %s\n", buf[5], (buf[5] == 1? "5.0V" : buf[5] == 2? "3.0V" : buf[5] == 3? "1.8V":"?")); us = convert_le_u32 (buf+6); DEBUGOUT_1 (" dwProtocols %5u ", us); if ((us & 1)) DEBUGOUT_CONT (" T=0"); if ((us & 2)) { DEBUGOUT_CONT (" T=1"); have_t1 = 1; } if ((us & ~3)) DEBUGOUT_CONT (" (Invalid values detected)"); DEBUGOUT_LF (); us = convert_le_u32(buf+10); DEBUGOUT_1 (" dwDefaultClock %5u\n", us); us = convert_le_u32(buf+14); DEBUGOUT_1 (" dwMaxiumumClock %5u\n", us); DEBUGOUT_1 (" bNumClockSupported %5u\n", buf[18]); us = convert_le_u32(buf+19); DEBUGOUT_1 (" dwDataRate %7u bps\n", us); us = convert_le_u32(buf+23); DEBUGOUT_1 (" dwMaxDataRate %7u bps\n", us); DEBUGOUT_1 (" bNumDataRatesSupp. %5u\n", buf[27]); us = convert_le_u32(buf+28); DEBUGOUT_1 (" dwMaxIFSD %5u\n", us); handle->max_ifsd = us; us = convert_le_u32(buf+32); DEBUGOUT_1 (" dwSyncProtocols %08X ", us); if ((us&1)) DEBUGOUT_CONT ( " 2-wire"); if ((us&2)) DEBUGOUT_CONT ( " 3-wire"); if ((us&4)) DEBUGOUT_CONT ( " I2C"); DEBUGOUT_LF (); us = convert_le_u32(buf+36); DEBUGOUT_1 (" dwMechanical %08X ", us); if ((us & 1)) DEBUGOUT_CONT (" accept"); if ((us & 2)) DEBUGOUT_CONT (" eject"); if ((us & 4)) DEBUGOUT_CONT (" capture"); if ((us & 8)) DEBUGOUT_CONT (" lock"); DEBUGOUT_LF (); us = convert_le_u32(buf+40); DEBUGOUT_1 (" dwFeatures %08X\n", us); if ((us & 0x0002)) { DEBUGOUT (" Auto configuration based on ATR\n"); have_auto_conf = 1; } if ((us & 0x0004)) DEBUGOUT (" Auto activation on insert\n"); if ((us & 0x0008)) DEBUGOUT (" Auto voltage selection\n"); if ((us & 0x0010)) DEBUGOUT (" Auto clock change\n"); if ((us & 0x0020)) DEBUGOUT (" Auto baud rate change\n"); if ((us & 0x0040)) DEBUGOUT (" Auto parameter negotiation made by CCID\n"); else if ((us & 0x0080)) DEBUGOUT (" Auto PPS made by CCID\n"); else if ((us & (0x0040 | 0x0080))) DEBUGOUT (" WARNING: conflicting negotiation features\n"); if ((us & 0x0100)) DEBUGOUT (" CCID can set ICC in clock stop mode\n"); if ((us & 0x0200)) { DEBUGOUT (" NAD value other than 0x00 accepted\n"); handle->nonnull_nad = 1; } if ((us & 0x0400)) { DEBUGOUT (" Auto IFSD exchange\n"); handle->auto_ifsd = 1; } if ((us & 0x00010000)) { DEBUGOUT (" TPDU level exchange\n"); have_tpdu = 1; } else if ((us & 0x00020000)) { DEBUGOUT (" Short APDU level exchange\n"); handle->apdu_level = 1; } else if ((us & 0x00040000)) { DEBUGOUT (" Short and extended APDU level exchange\n"); handle->apdu_level = 2; } else if ((us & 0x00070000)) DEBUGOUT (" WARNING: conflicting exchange levels\n"); us = convert_le_u32(buf+44); DEBUGOUT_1 (" dwMaxCCIDMsgLen %5u\n", us); DEBUGOUT ( " bClassGetResponse "); if (buf[48] == 0xff) DEBUGOUT_CONT ("echo\n"); else DEBUGOUT_CONT_1 (" %02X\n", buf[48]); DEBUGOUT ( " bClassEnvelope "); if (buf[49] == 0xff) DEBUGOUT_CONT ("echo\n"); else DEBUGOUT_CONT_1 (" %02X\n", buf[48]); DEBUGOUT ( " wlcdLayout "); if (!buf[50] && !buf[51]) DEBUGOUT_CONT ("none\n"); else DEBUGOUT_CONT_2 ("%u cols %u lines\n", buf[50], buf[51]); DEBUGOUT_1 (" bPINSupport %5u ", buf[52]); if ((buf[52] & 1)) { DEBUGOUT_CONT ( " verification"); handle->has_pinpad |= 1; } if ((buf[52] & 2)) { DEBUGOUT_CONT ( " modification"); handle->has_pinpad |= 2; } DEBUGOUT_LF (); DEBUGOUT_1 (" bMaxCCIDBusySlots %5u\n", buf[53]); if (buf[0] > 54) { DEBUGOUT (" junk "); for (i=54; i < buf[0]-54; i++) DEBUGOUT_CONT_1 (" %02X", buf[i]); DEBUGOUT_LF (); } if (!have_t1 || !(have_tpdu || handle->apdu_level) || !have_auto_conf) { DEBUGOUT ("this drivers requires that the reader supports T=1, " "TPDU or APDU level exchange and auto configuration - " "this is not available\n"); return -1; } /* SCM drivers get stuck in their internal USB stack if they try to send a frame of n*wMaxPacketSize back to us. Given that wMaxPacketSize is 64 for these readers we set the IFSD to a value lower than that: 64 - 10 CCID header - 4 T1frame - 2 reserved = 48 Product Ids: 0xe001 - SCR 331 0x5111 - SCR 331-DI 0x5115 - SCR 335 0xe003 - SPR 532 The 0x5117 - SCR 3320 USB ID-000 reader seems to be very slow but enabling this workaround boosts the performance to a a more or less acceptable level (tested by David). */ if (handle->id_vendor == VENDOR_SCM && handle->max_ifsd > 48 && ( (handle->id_product == 0xe001 && handle->bcd_device < 0x0516) ||(handle->id_product == 0x5111 && handle->bcd_device < 0x0620) ||(handle->id_product == 0x5115 && handle->bcd_device < 0x0514) ||(handle->id_product == 0xe003 && handle->bcd_device < 0x0504) ||(handle->id_product == 0x5117 && handle->bcd_device < 0x0522) )) { DEBUGOUT ("enabling workaround for buggy SCM readers\n"); handle->max_ifsd = 48; } return 0; } static char * get_escaped_usb_string (usb_dev_handle *idev, int idx, const char *prefix, const char *suffix) { int rc; unsigned char buf[280]; unsigned char *s; unsigned int langid; size_t i, n, len; char *result; if (!idx) return NULL; /* Fixme: The next line is for the current Valgrid without support for USB IOCTLs. */ memset (buf, 0, sizeof buf); /* First get the list of supported languages and use the first one. If we do don't find it we try to use English. Note that this is all in a 2 bute Unicode encoding using little endian. */ rc = usb_control_msg (idev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8), 0, (char*)buf, sizeof buf, 1000 /* ms timeout */); if (rc < 4) langid = 0x0409; /* English. */ else langid = (buf[3] << 8) | buf[2]; rc = usb_control_msg (idev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + idx, langid, (char*)buf, sizeof buf, 1000 /* ms timeout */); if (rc < 2 || buf[1] != USB_DT_STRING) return NULL; /* Error or not a string. */ len = buf[0]; if (len > rc) return NULL; /* Larger than our buffer. */ for (s=buf+2, i=2, n=0; i+1 < len; i += 2, s += 2) { if (s[1]) n++; /* High byte set. */ else if (*s <= 0x20 || *s >= 0x7f || *s == '%' || *s == ':') n += 3 ; else n++; } result = malloc (strlen (prefix) + n + strlen (suffix) + 1); if (!result) return NULL; strcpy (result, prefix); n = strlen (prefix); for (s=buf+2, i=2; i+1 < len; i += 2, s += 2) { if (s[1]) result[n++] = '\xff'; /* High byte set. */ else if (*s <= 0x20 || *s >= 0x7f || *s == '%' || *s == ':') { sprintf (result+n, "%%%02X", *s); n += 3; } else result[n++] = *s; } strcpy (result+n, suffix); return result; } /* This function creates an reader id to be used to find the same physical reader after a reset. It returns an allocated and possibly percent escaped string or NULL if not enough memory is available. */ static char * make_reader_id (usb_dev_handle *idev, unsigned int vendor, unsigned int product, unsigned char serialno_index) { char *rid; char prefix[20]; sprintf (prefix, "%04X:%04X:", (vendor & 0xffff), (product & 0xffff)); rid = get_escaped_usb_string (idev, serialno_index, prefix, ":0"); if (!rid) { rid = malloc (strlen (prefix) + 3 + 1); if (!rid) return NULL; strcpy (rid, prefix); strcat (rid, "X:0"); } return rid; } /* Helper to find the endpoint from an interface descriptor. */ static int find_endpoint (struct usb_interface_descriptor *ifcdesc, int mode) { int no; int want_bulk_in = 0; if (mode == 1) want_bulk_in = 0x80; for (no=0; no < ifcdesc->bNumEndpoints; no++) { struct usb_endpoint_descriptor *ep = ifcdesc->endpoint + no; if (ep->bDescriptorType != USB_DT_ENDPOINT) ; else if (mode == 2 && ((ep->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT) && (ep->bEndpointAddress & 0x80)) return (ep->bEndpointAddress & 0x0f); else if (((ep->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK) && (ep->bEndpointAddress & 0x80) == want_bulk_in) return (ep->bEndpointAddress & 0x0f); } /* Should never happen. */ return mode == 2? 0x83 : mode == 1? 0x82 :1; } /* Helper for scan_or_find_devices. This function returns true if a requested device has been found or the caller should stop scanning for other reasons. */ static int scan_or_find_usb_device (int scan_mode, int *readerno, int *count, char **rid_list, const char *readerid, struct usb_device *dev, char **r_rid, struct usb_device **r_dev, usb_dev_handle **r_idev, unsigned char **ifcdesc_extra, size_t *ifcdesc_extra_len, int *interface_number, int *ep_bulk_out, int *ep_bulk_in, int *ep_intr) { int cfg_no; int ifc_no; int set_no; struct usb_config_descriptor *config; struct usb_interface *interface; struct usb_interface_descriptor *ifcdesc; char *rid; usb_dev_handle *idev; *r_idev = NULL; for (cfg_no=0; cfg_no < dev->descriptor.bNumConfigurations; cfg_no++) { config = dev->config + cfg_no; if(!config) continue; for (ifc_no=0; ifc_no < config->bNumInterfaces; ifc_no++) { interface = config->interface + ifc_no; if (!interface) continue; for (set_no=0; set_no < interface->num_altsetting; set_no++) { ifcdesc = (interface->altsetting + set_no); /* The second condition is for older SCM SPR 532 who did not know about the assigned CCID class. Instead of trying to interpret the strings we simply check the product ID. */ if (ifcdesc && ifcdesc->extra && ((ifcdesc->bInterfaceClass == 11 && ifcdesc->bInterfaceSubClass == 0 && ifcdesc->bInterfaceProtocol == 0) || (ifcdesc->bInterfaceClass == 255 && dev->descriptor.idVendor == VENDOR_SCM && dev->descriptor.idProduct == 0xe003))) { idev = usb_open (dev); if (!idev) { DEBUGOUT_1 ("usb_open failed: %s\n", strerror (errno)); continue; /* with next setting. */ } rid = make_reader_id (idev, dev->descriptor.idVendor, dev->descriptor.idProduct, dev->descriptor.iSerialNumber); if (rid) { if (scan_mode) { char *p; /* We are collecting infos about all available CCID readers. Store them and continue. */ DEBUGOUT_2 ("found CCID reader %d (ID=%s)\n", *count, rid ); p = malloc ((*rid_list? strlen (*rid_list):0) + 1 + strlen (rid) + 1); if (p) { *p = 0; if (*rid_list) { strcat (p, *rid_list); free (*rid_list); } strcat (p, rid); strcat (p, "\n"); *rid_list = p; } else /* Out of memory. */ free (rid); rid = NULL; ++*count; } else if (!*readerno || (*readerno < 0 && readerid && !strcmp (readerid, rid))) { /* We found the requested reader. */ if (ifcdesc_extra && ifcdesc_extra_len) { *ifcdesc_extra = malloc (ifcdesc ->extralen); if (!*ifcdesc_extra) { usb_close (idev); free (rid); return 1; /* Out of core. */ } memcpy (*ifcdesc_extra, ifcdesc->extra, ifcdesc->extralen); *ifcdesc_extra_len = ifcdesc->extralen; } if (interface_number) *interface_number = (ifcdesc->bInterfaceNumber); if (ep_bulk_out) *ep_bulk_out = find_endpoint (ifcdesc, 0); if (ep_bulk_in) *ep_bulk_in = find_endpoint (ifcdesc, 1); if (ep_intr) *ep_intr = find_endpoint (ifcdesc, 2); if (r_dev) *r_dev = dev; if (r_rid) { *r_rid = rid; rid = NULL; } else free (rid); *r_idev = idev; return 1; /* Found requested device. */ } else { /* This is not yet the reader we want. fixme: We should avoid the extra usb_open in this case. */ if (*readerno >= 0) --*readerno; } free (rid); } usb_close (idev); idev = NULL; return 0; } } } } return 0; } /* Combination function to either scan all CCID devices or to find and open one specific device. The function returns 0 if a reader has been found or when a scan returned without error. With READERNO = -1 and READERID is NULL, scan mode is used and R_RID should be the address where to store the list of reader_ids we found. If on return this list is empty, no CCID device has been found; otherwise it points to an allocated linked list of reader IDs. Note that in this mode the function always returns NULL. With READERNO >= 0 or READERID is not NULL find mode is used. This uses the same algorithm as the scan mode but stops and returns at the entry number READERNO and return the handle for the the opened USB device. If R_RID is not NULL it will receive the reader ID of that device. If R_DEV is not NULL it will the device pointer of that device. If IFCDESC_EXTRA is NOT NULL it will receive a malloced copy of the interfaces "extra: data filed; IFCDESC_EXTRA_LEN receive the length of this field. If there is no reader with number READERNO or that reader is not usable by our implementation NULL will be returned. The caller must close a returned USB device handle and free (if not passed as NULL) the returned reader ID info as well as the IFCDESC_EXTRA. On error NULL will get stored at R_RID, R_DEV, IFCDESC_EXTRA and IFCDESC_EXTRA_LEN. With READERID being -1 the function stops if the READERID was found. If R_FD is not -1 on return the device is not using USB for transport but the device associated with that file descriptor. In this case INTERFACE will receive the transport type and the other USB specific return values are not used; the return value is (void*)(1). Note that the first entry of the returned reader ID list in scan mode corresponds with a READERNO of 0 in find mode. */ static int scan_or_find_devices (int readerno, const char *readerid, char **r_rid, struct usb_device **r_dev, unsigned char **ifcdesc_extra, size_t *ifcdesc_extra_len, int *interface_number, int *ep_bulk_out, int *ep_bulk_in, int *ep_intr, usb_dev_handle **r_idev, int *r_fd) { char *rid_list = NULL; int count = 0; struct usb_bus *busses, *bus; struct usb_device *dev = NULL; usb_dev_handle *idev = NULL; int scan_mode = (readerno == -1 && !readerid); int i; /* Set return values to a default. */ if (r_rid) *r_rid = NULL; if (r_dev) *r_dev = NULL; if (ifcdesc_extra) *ifcdesc_extra = NULL; if (ifcdesc_extra_len) *ifcdesc_extra_len = 0; if (interface_number) *interface_number = 0; if (r_idev) *r_idev = NULL; if (r_fd) *r_fd = -1; /* See whether we want scan or find mode. */ if (scan_mode) { assert (r_rid); } usb_find_busses(); usb_find_devices(); #ifdef HAVE_USB_GET_BUSSES busses = usb_get_busses(); #else busses = usb_busses; #endif for (bus = busses; bus; bus = bus->next) { for (dev = bus->devices; dev; dev = dev->next) { if (scan_or_find_usb_device (scan_mode, &readerno, &count, &rid_list, readerid, dev, r_rid, r_dev, &idev, ifcdesc_extra, ifcdesc_extra_len, interface_number, ep_bulk_out, ep_bulk_in, ep_intr)) { /* Found requested device or out of core. */ if (!idev) { free (rid_list); return -1; /* error */ } *r_idev = idev; return 0; } } } /* Now check whether there are any devices with special transport types. */ for (i=0; transports[i].name; i++) { int fd; char *rid, *p; fd = open (transports[i].name, O_RDWR); if (fd == -1 && scan_mode && errno == EBUSY) { /* Ignore this error in scan mode because it indicates that the device exists but is already open (most likely by us) and thus in general suitable as a reader. */ } else if (fd == -1) { DEBUGOUT_2 ("failed to open `%s': %s\n", transports[i].name, strerror (errno)); continue; } rid = malloc (strlen (transports[i].name) + 30 + 10); if (!rid) { if (fd != -1) close (fd); free (rid_list); return -1; /* Error. */ } sprintf (rid, "0000:%04X:%s:0", transports[i].type, transports[i].name); if (scan_mode) { DEBUGOUT_2 ("found CCID reader %d (ID=%s)\n", count, rid); p = malloc ((rid_list? strlen (rid_list):0) + 1 + strlen (rid) + 1); if (!p) { if (fd != -1) close (fd); free (rid_list); free (rid); return -1; /* Error. */ } *p = 0; if (rid_list) { strcat (p, rid_list); free (rid_list); } strcat (p, rid); strcat (p, "\n"); rid_list = p; ++count; } else if (!readerno || (readerno < 0 && readerid && !strcmp (readerid, rid))) { /* Found requested device. */ if (interface_number) *interface_number = transports[i].type; if (r_rid) *r_rid = rid; else free (rid); if (r_fd) *r_fd = fd; return 0; /* Okay, found device */ } else /* This is not yet the reader we want. */ { if (readerno >= 0) --readerno; } free (rid); if (fd != -1) close (fd); } if (scan_mode) { *r_rid = rid_list; return 0; } else return -1; } /* Set the level of debugging to LEVEL and return the old level. -1 just returns the old level. A level of 0 disables debugging, 1 enables debugging, 2 enables additional tracing of the T=1 protocol, 3 additionally enables debugging for GetSlotStatus, other values are not yet defined. Note that libusb may provide its own debugging feature which is enabled by setting the envvar USB_DEBUG. */ int ccid_set_debug_level (int level) { int old = debug_level; if (level != -1) debug_level = level; return old; } char * ccid_get_reader_list (void) { char *reader_list; if (!initialized_usb) { usb_init (); initialized_usb = 1; } if (scan_or_find_devices (-1, NULL, &reader_list, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) return NULL; /* Error. */ return reader_list; } /* Open the reader with the internal number READERNO and return a pointer to be used as handle in HANDLE. Returns 0 on success. */ int ccid_open_reader (ccid_driver_t *handle, const char *readerid) { int rc = 0; struct usb_device *dev = NULL; usb_dev_handle *idev = NULL; int dev_fd = -1; char *rid = NULL; unsigned char *ifcdesc_extra = NULL; size_t ifcdesc_extra_len; int readerno; int ifc_no, ep_bulk_out, ep_bulk_in, ep_intr; *handle = NULL; if (!initialized_usb) { usb_init (); initialized_usb = 1; } /* See whether we want to use the reader ID string or a reader number. A readerno of -1 indicates that the reader ID string is to be used. */ if (readerid && strchr (readerid, ':')) readerno = -1; /* We want to use the readerid. */ else if (readerid) { readerno = atoi (readerid); if (readerno < 0) { DEBUGOUT ("no CCID readers found\n"); rc = CCID_DRIVER_ERR_NO_READER; goto leave; } } else readerno = 0; /* Default. */ if (scan_or_find_devices (readerno, readerid, &rid, &dev, &ifcdesc_extra, &ifcdesc_extra_len, &ifc_no, &ep_bulk_out, &ep_bulk_in, &ep_intr, &idev, &dev_fd) ) { if (readerno == -1) DEBUGOUT_1 ("no CCID reader with ID %s\n", readerid ); else DEBUGOUT_1 ("no CCID reader with number %d\n", readerno ); rc = CCID_DRIVER_ERR_NO_READER; goto leave; } /* Okay, this is a CCID reader. */ *handle = calloc (1, sizeof **handle); if (!*handle) { DEBUGOUT ("out of memory\n"); rc = CCID_DRIVER_ERR_OUT_OF_CORE; goto leave; } (*handle)->rid = rid; if (idev) /* Regular USB transport. */ { (*handle)->idev = idev; (*handle)->dev_fd = -1; (*handle)->id_vendor = dev->descriptor.idVendor; (*handle)->id_product = dev->descriptor.idProduct; (*handle)->bcd_device = dev->descriptor.bcdDevice; (*handle)->ifc_no = ifc_no; (*handle)->ep_bulk_out = ep_bulk_out; (*handle)->ep_bulk_in = ep_bulk_in; (*handle)->ep_intr = ep_intr; } else if (dev_fd != -1) /* Device transport. */ { (*handle)->idev = NULL; (*handle)->dev_fd = dev_fd; (*handle)->id_vendor = 0; /* Magic vendor for special transport. */ (*handle)->id_product = ifc_no; /* Transport type */ prepare_special_transport (*handle); } else { assert (!"no transport"); /* Bug. */ } DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n", readerno, rid ); if (idev) { if (parse_ccid_descriptor (*handle, ifcdesc_extra, ifcdesc_extra_len)) { DEBUGOUT ("device not supported\n"); rc = CCID_DRIVER_ERR_NO_READER; goto leave; } rc = usb_claim_interface (idev, ifc_no); if (rc) { DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc); rc = CCID_DRIVER_ERR_CARD_IO_ERROR; goto leave; } } leave: free (ifcdesc_extra); if (rc) { free (rid); if (idev) usb_close (idev); if (dev_fd != -1) close (dev_fd); free (*handle); *handle = NULL; } return rc; } static void do_close_reader (ccid_driver_t handle) { int rc; unsigned char msg[100]; size_t msglen; unsigned char seqno; if (!handle->powered_off) { msg[0] = PC_to_RDR_IccPowerOff; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 0; /* RFU */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ set_msg_len (msg, 0); msglen = 10; rc = bulk_out (handle, msg, msglen, 0); if (!rc) bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno, 2000, 0); handle->powered_off = 1; } if (handle->idev) { usb_release_interface (handle->idev, handle->ifc_no); usb_close (handle->idev); handle->idev = NULL; } if (handle->dev_fd != -1) { close (handle->dev_fd); handle->dev_fd = -1; } } /* Reset a reader on HANDLE. This is useful in case a reader has been plugged of and inserted at a different port. By resetting the handle, the same reader will be get used. Note, that on error the handle won't get released. This does not return an ATR, so ccid_get_atr should be called right after this one. */ int ccid_shutdown_reader (ccid_driver_t handle) { int rc = 0; struct usb_device *dev = NULL; usb_dev_handle *idev = NULL; unsigned char *ifcdesc_extra = NULL; size_t ifcdesc_extra_len; int ifc_no, ep_bulk_out, ep_bulk_in, ep_intr; if (!handle || !handle->rid) return CCID_DRIVER_ERR_INV_VALUE; do_close_reader (handle); if (scan_or_find_devices (-1, handle->rid, NULL, &dev, &ifcdesc_extra, &ifcdesc_extra_len, &ifc_no, &ep_bulk_out, &ep_bulk_in, &ep_intr, &idev, NULL) || !idev) { DEBUGOUT_1 ("no CCID reader with ID %s\n", handle->rid); return CCID_DRIVER_ERR_NO_READER; } if (idev) { handle->idev = idev; handle->ifc_no = ifc_no; handle->ep_bulk_out = ep_bulk_out; handle->ep_bulk_in = ep_bulk_in; handle->ep_intr = ep_intr; if (parse_ccid_descriptor (handle, ifcdesc_extra, ifcdesc_extra_len)) { DEBUGOUT ("device not supported\n"); rc = CCID_DRIVER_ERR_NO_READER; goto leave; } rc = usb_claim_interface (idev, ifc_no); if (rc) { DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc); rc = CCID_DRIVER_ERR_CARD_IO_ERROR; goto leave; } } leave: free (ifcdesc_extra); if (rc) { if (handle->idev) usb_close (handle->idev); handle->idev = NULL; if (handle->dev_fd != -1) close (handle->dev_fd); handle->dev_fd = -1; } return rc; } int ccid_set_progress_cb (ccid_driver_t handle, void (*cb)(void *, const char *, int, int, int), void *cb_arg) { if (!handle || !handle->rid) return CCID_DRIVER_ERR_INV_VALUE; handle->progress_cb = cb; handle->progress_cb_arg = cb_arg; return 0; } /* Close the reader HANDLE. */ int ccid_close_reader (ccid_driver_t handle) { if (!handle || (!handle->idev && handle->dev_fd == -1)) return 0; do_close_reader (handle); free (handle->rid); free (handle); return 0; } /* Return False if a card is present and powered. */ int ccid_check_card_presence (ccid_driver_t handle) { (void)handle; /* Not yet implemented. */ return -1; } /* 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 return -1; } nleft -= nwritten; buf = (const char*)buf + nwritten; } return 0; } /* Write a MSG of length MSGLEN to the designated bulk out endpoint. Returns 0 on success. */ static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen, int no_debug) { int rc; /* No need to continue and clutter the log with USB write error messages after we got the first ENODEV. */ if (handle->enodev_seen) return CCID_DRIVER_ERR_NO_READER; if (debug_level && (!no_debug || debug_level >= 3)) { switch (msglen? msg[0]:0) { case PC_to_RDR_IccPowerOn: print_p2r_iccpoweron (msg, msglen); break; case PC_to_RDR_IccPowerOff: print_p2r_iccpoweroff (msg, msglen); break; case PC_to_RDR_GetSlotStatus: print_p2r_getslotstatus (msg, msglen); break; case PC_to_RDR_XfrBlock: print_p2r_xfrblock (msg, msglen); break; case PC_to_RDR_GetParameters: print_p2r_getparameters (msg, msglen); break; case PC_to_RDR_ResetParameters: print_p2r_resetparameters (msg, msglen); break; case PC_to_RDR_SetParameters: print_p2r_setparameters (msg, msglen); break; case PC_to_RDR_Escape: print_p2r_escape (msg, msglen); break; case PC_to_RDR_IccClock: print_p2r_iccclock (msg, msglen); break; case PC_to_RDR_T0APDU: print_p2r_to0apdu (msg, msglen); break; case PC_to_RDR_Secure: print_p2r_secure (msg, msglen); break; case PC_to_RDR_Mechanical: print_p2r_mechanical (msg, msglen); break; case PC_to_RDR_Abort: print_p2r_abort (msg, msglen); break; case PC_to_RDR_SetDataRate: print_p2r_setdatarate (msg, msglen); break; default: print_p2r_unknown (msg, msglen); break; } } if (handle->idev) { rc = usb_bulk_write (handle->idev, handle->ep_bulk_out, (char*)msg, msglen, 5000 /* ms timeout */); if (rc == msglen) return 0; #ifdef ENODEV if (rc == -(ENODEV)) { /* The Linux libusb returns a negative error value. Catch the most important one. */ errno = ENODEV; rc = -1; } #endif /*ENODEV*/ if (rc == -1) { DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno)); #ifdef ENODEV if (errno == ENODEV) { handle->enodev_seen = 1; return CCID_DRIVER_ERR_NO_READER; } #endif /*ENODEV*/ } else DEBUGOUT_1 ("usb_bulk_write failed: %d\n", rc); } else { rc = writen (handle->dev_fd, msg, msglen); if (!rc) return 0; DEBUGOUT_2 ("writen to %d failed: %s\n", handle->dev_fd, strerror (errno)); } return CCID_DRIVER_ERR_CARD_IO_ERROR; } /* Read a maximum of LENGTH bytes from the bulk in endpoint into BUFFER and return the actual read number if bytes in NREAD. SEQNO is the sequence number used to send the request and EXPECTED_TYPE the type of message we expect. Does checks on the ccid header. TIMEOUT is the timeout value in ms. NO_DEBUG may be set to avoid debug messages in case of no error; this can be overriden with a glibal debug level of at least 3. Returns 0 on success. */ static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length, size_t *nread, int expected_type, int seqno, int timeout, int no_debug) { int rc; size_t msglen; int eagain_retries = 0; /* Fixme: The next line for the current Valgrind without support for USB IOCTLs. */ memset (buffer, 0, length); retry: if (handle->idev) { rc = usb_bulk_read (handle->idev, handle->ep_bulk_in, (char*)buffer, length, timeout); if (rc < 0) { rc = errno; DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (rc)); if (rc == EAGAIN && eagain_retries++ < 3) { my_sleep (1); goto retry; } return CCID_DRIVER_ERR_CARD_IO_ERROR; } *nread = msglen = rc; } else { rc = read (handle->dev_fd, buffer, length); if (rc < 0) { rc = errno; DEBUGOUT_2 ("read from %d failed: %s\n", handle->dev_fd, strerror (rc)); if (rc == EAGAIN && eagain_retries++ < 5) { my_sleep (1); goto retry; } return CCID_DRIVER_ERR_CARD_IO_ERROR; } *nread = msglen = rc; } eagain_retries = 0; if (msglen < 10) { DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen); abort_cmd (handle, seqno); return CCID_DRIVER_ERR_INV_VALUE; } if (buffer[5] != 0) { DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]); return CCID_DRIVER_ERR_INV_VALUE; } if (buffer[6] != seqno) { DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n", seqno, buffer[6]); /* Retry until we are synced again. */ goto retry; } /* We need to handle the time extension request before we check that we got the expected message type. This is in particular required for the Cherry keyboard which sends a time extension request for each key hit. */ if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80) { /* Card present and active, time extension requested. */ DEBUGOUT_2 ("time extension requested (%02X,%02X)\n", buffer[7], buffer[8]); goto retry; } if (buffer[0] != expected_type) { DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]); abort_cmd (handle, seqno); return CCID_DRIVER_ERR_INV_VALUE; } if (debug_level && (!no_debug || debug_level >= 3)) { switch (buffer[0]) { case RDR_to_PC_DataBlock: print_r2p_datablock (buffer, msglen); break; case RDR_to_PC_SlotStatus: print_r2p_slotstatus (buffer, msglen); break; case RDR_to_PC_Parameters: print_r2p_parameters (buffer, msglen); break; case RDR_to_PC_Escape: print_r2p_escape (buffer, msglen); break; case RDR_to_PC_DataRate: print_r2p_datarate (buffer, msglen); break; default: print_r2p_unknown (buffer, msglen); break; } } if (CCID_COMMAND_FAILED (buffer)) print_command_failed (buffer); /* Check whether a card is at all available. Note: If you add new error codes here, check whether they need to be ignored in send_escape_cmd. */ switch ((buffer[7] & 0x03)) { case 0: /* no error */ break; case 1: return CCID_DRIVER_ERR_CARD_INACTIVE; case 2: return CCID_DRIVER_ERR_NO_CARD; case 3: /* RFU */ break; } return 0; } /* Send an abort sequence and wait until everything settled. */ static int abort_cmd (ccid_driver_t handle, int seqno) { int rc; char dummybuf[8]; unsigned char msg[100]; size_t msglen; if (!handle->idev) { /* I don't know how to send an abort to non-USB devices. */ rc = CCID_DRIVER_ERR_NOT_SUPPORTED; } seqno &= 0xff; DEBUGOUT_1 ("sending abort sequence for seqno %d\n", seqno); /* Send the abort command to the control pipe. Note that we don't need to keep track of sent abort commands because there should never be another thread using the same slot concurrently. */ rc = usb_control_msg (handle->idev, 0x21,/* bmRequestType: host-to-device, class specific, to interface. */ 1, /* ABORT */ (seqno << 8 | 0 /* slot */), handle->ifc_no, dummybuf, 0, 1000 /* ms timeout */); if (rc < 0) { DEBUGOUT_1 ("usb_control_msg error: %s\n", strerror (errno)); return CCID_DRIVER_ERR_CARD_IO_ERROR; } /* Now send the abort command to the bulk out pipe using the same SEQNO and SLOT. Do this in a loop to so that all seqno are tried. */ seqno--; /* Adjust for next increment. */ do { seqno++; msg[0] = PC_to_RDR_Abort; msg[5] = 0; /* slot */ msg[6] = seqno; msg[7] = 0; /* RFU */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ msglen = 10; set_msg_len (msg, 0); rc = usb_bulk_write (handle->idev, handle->ep_bulk_out, (char*)msg, msglen, 5000 /* ms timeout */); if (rc == msglen) rc = 0; else if (rc == -1) DEBUGOUT_1 ("usb_bulk_write error in abort_cmd: %s\n", strerror (errno)); else DEBUGOUT_1 ("usb_bulk_write failed in abort_cmd: %d\n", rc); if (rc) return rc; rc = usb_bulk_read (handle->idev, handle->ep_bulk_in, (char*)msg, sizeof msg, 5000 /*ms timeout*/); if (rc < 0) { DEBUGOUT_1 ("usb_bulk_read error in abort_cmd: %s\n", strerror (errno)); return CCID_DRIVER_ERR_CARD_IO_ERROR; } msglen = rc; if (msglen < 10) { DEBUGOUT_1 ("bulk-in msg in abort_cmd too short (%u)\n", (unsigned int)msglen); return CCID_DRIVER_ERR_INV_VALUE; } if (msg[5] != 0) { DEBUGOUT_1 ("unexpected bulk-in slot (%d) in abort_cmd\n", msg[5]); return CCID_DRIVER_ERR_INV_VALUE; } DEBUGOUT_3 ("status: %02X error: %02X octet[9]: %02X\n", msg[7], msg[8], msg[9]); if (CCID_COMMAND_FAILED (msg)) print_command_failed (msg); } while (msg[0] != RDR_to_PC_SlotStatus && msg[5] != 0 && msg[6] != seqno); handle->seqno = ((seqno + 1) & 0xff); DEBUGOUT ("sending abort sequence succeeded\n"); return 0; } /* Note that this function won't return the error codes NO_CARD or CARD_INACTIVE. IF RESULT is not NULL, the result from the operation will get returned in RESULT and its length in RESULTLEN. If the response is larger than RESULTMAX, an error is returned and the required buffer length returned in RESULTLEN. */ static int send_escape_cmd (ccid_driver_t handle, const unsigned char *data, size_t datalen, unsigned char *result, size_t resultmax, size_t *resultlen) { int rc; unsigned char msg[100]; size_t msglen; unsigned char seqno; if (resultlen) *resultlen = 0; if (datalen > sizeof msg - 10) return CCID_DRIVER_ERR_INV_VALUE; /* Escape data too large. */ msg[0] = PC_to_RDR_Escape; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 0; /* RFU */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ memcpy (msg+10, data, datalen); msglen = 10 + datalen; set_msg_len (msg, datalen); rc = bulk_out (handle, msg, msglen, 0); if (rc) return rc; rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Escape, seqno, 5000, 0); if (result) switch (rc) { /* We need to ignore certain errorcode here. */ case 0: case CCID_DRIVER_ERR_CARD_INACTIVE: case CCID_DRIVER_ERR_NO_CARD: { if (msglen > resultmax) rc = CCID_DRIVER_ERR_INV_VALUE; /* Response too large. */ else { memcpy (result, msg, msglen); *resultlen = msglen; } rc = 0; } break; default: break; } return rc; } int ccid_transceive_escape (ccid_driver_t handle, const unsigned char *data, size_t datalen, unsigned char *resp, size_t maxresplen, size_t *nresp) { return send_escape_cmd (handle, data, datalen, resp, maxresplen, nresp); } /* experimental */ int ccid_poll (ccid_driver_t handle) { int rc; unsigned char msg[10]; size_t msglen; int i, j; if (handle->idev) { rc = usb_bulk_read (handle->idev, handle->ep_intr, (char*)msg, sizeof msg, 0 /* ms timeout */ ); if (rc < 0 && errno == ETIMEDOUT) return 0; } else return 0; if (rc < 0) { DEBUGOUT_1 ("usb_intr_read error: %s\n", strerror (errno)); return CCID_DRIVER_ERR_CARD_IO_ERROR; } msglen = rc; rc = 0; if (msglen < 1) { DEBUGOUT ("intr-in msg too short\n"); return CCID_DRIVER_ERR_INV_VALUE; } if (msg[0] == RDR_to_PC_NotifySlotChange) { DEBUGOUT ("notify slot change:"); for (i=1; i < msglen; i++) for (j=0; j < 4; j++) DEBUGOUT_CONT_3 (" %d:%c%c", (i-1)*4+j, (msg[i] & (1<<(j*2)))? 'p':'-', (msg[i] & (2<<(j*2)))? '*':' '); DEBUGOUT_LF (); } else if (msg[0] == RDR_to_PC_HardwareError) { DEBUGOUT ("hardware error occured\n"); } else { DEBUGOUT_1 ("unknown intr-in msg of type %02X\n", msg[0]); } return 0; } /* Note that this function won't return the error codes NO_CARD or CARD_INACTIVE */ int ccid_slot_status (ccid_driver_t handle, int *statusbits) { int rc; unsigned char msg[100]; size_t msglen; unsigned char seqno; int retries = 0; retry: msg[0] = PC_to_RDR_GetSlotStatus; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 0; /* RFU */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ set_msg_len (msg, 0); rc = bulk_out (handle, msg, 10, 1); if (rc) return rc; /* Note that we set the NO_DEBUG flag here, so that the logs won't get cluttered up by a ticker function checking for the slot status and debugging enabled. */ rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno, retries? 1000 : 200, 1); if (rc == CCID_DRIVER_ERR_CARD_IO_ERROR && retries < 3) { if (!retries) { DEBUGOUT ("USB: CALLING USB_CLEAR_HALT\n"); usb_clear_halt (handle->idev, handle->ep_bulk_in); usb_clear_halt (handle->idev, handle->ep_bulk_out); } else DEBUGOUT ("USB: RETRYING bulk_in AGAIN\n"); retries++; goto retry; } if (rc && rc != CCID_DRIVER_ERR_NO_CARD && rc != CCID_DRIVER_ERR_CARD_INACTIVE) return rc; *statusbits = (msg[7] & 3); return 0; } /* Return the ATR of the card. This is not a cached value and thus an actual reset is done. */ int ccid_get_atr (ccid_driver_t handle, unsigned char *atr, size_t maxatrlen, size_t *atrlen) { int rc; int statusbits; unsigned char msg[100]; unsigned char *tpdu; size_t msglen, tpdulen; unsigned char seqno; int use_crc = 0; unsigned int edc; int tried_iso = 0; int got_param; /* First check whether a card is available. */ rc = ccid_slot_status (handle, &statusbits); if (rc) return rc; if (statusbits == 2) return CCID_DRIVER_ERR_NO_CARD; /* For an inactive and also for an active card, issue the PowerOn command to get the ATR. */ again: msg[0] = PC_to_RDR_IccPowerOn; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 0; /* power select (0=auto, 1=5V, 2=3V, 3=1.8V) */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ set_msg_len (msg, 0); msglen = 10; rc = bulk_out (handle, msg, msglen, 0); if (rc) return rc; rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock, seqno, 5000, 0); if (rc) return rc; if (!tried_iso && CCID_COMMAND_FAILED (msg) && CCID_ERROR_CODE (msg) == 0xbb && ((handle->id_vendor == VENDOR_CHERRY && handle->id_product == 0x0005) || (handle->id_vendor == VENDOR_GEMPC && handle->id_product == 0x4433) )) { tried_iso = 1; /* Try switching to ISO mode. */ if (!send_escape_cmd (handle, (const unsigned char*)"\xF1\x01", 2, NULL, 0, NULL)) goto again; } else if (CCID_COMMAND_FAILED (msg)) return CCID_DRIVER_ERR_CARD_IO_ERROR; handle->powered_off = 0; if (atr) { size_t n = msglen - 10; if (n > maxatrlen) n = maxatrlen; memcpy (atr, msg+10, n); *atrlen = n; } got_param = 0; msg[0] = PC_to_RDR_GetParameters; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 0; /* RFU */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ set_msg_len (msg, 0); msglen = 10; rc = bulk_out (handle, msg, msglen, 0); if (!rc) rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters, seqno, 2000, 0); if (rc) DEBUGOUT ("GetParameters failed\n"); else if (msglen == 17 && msg[9] == 1) got_param = 1; /* Setup parameters to select T=1. */ msg[0] = PC_to_RDR_SetParameters; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 1; /* Select T=1. */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ if (!got_param) { /* FIXME: Get those values from the ATR. */ msg[10]= 0x01; /* Fi/Di */ msg[11]= 0x10; /* LRC, direct convention. */ msg[12]= 0; /* Extra guardtime. */ msg[13]= 0x41; /* BWI/CWI */ msg[14]= 0; /* No clock stoppping. */ msg[15]= 254; /* IFSC */ msg[16]= 0; /* Does not support non default NAD values. */ } set_msg_len (msg, 7); msglen = 10 + 7; rc = bulk_out (handle, msg, msglen, 0); if (rc) return rc; rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters, seqno, 5000, 0); if (rc) DEBUGOUT ("SetParameters failed (ignored)\n"); if (!rc && msglen > 15 && msg[15] >= 16 && msg[15] <= 254 ) handle->ifsc = msg[15]; else handle->ifsc = 128; /* Something went wrong, assume 128 bytes. */ handle->t1_ns = 0; handle->t1_nr = 0; /* Send an S-Block with our maximum IFSD to the CCID. */ if (!handle->apdu_level && !handle->auto_ifsd) { tpdu = msg+10; /* NAD: DAD=1, SAD=0 */ tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0; tpdu[1] = (0xc0 | 0 | 1); /* S-block request: change IFSD */ tpdu[2] = 1; tpdu[3] = handle->max_ifsd? handle->max_ifsd : 32; tpdulen = 4; edc = compute_edc (tpdu, tpdulen, use_crc); if (use_crc) tpdu[tpdulen++] = (edc >> 8); tpdu[tpdulen++] = edc; msg[0] = PC_to_RDR_XfrBlock; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 0; msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ set_msg_len (msg, tpdulen); msglen = 10 + tpdulen; if (debug_level > 1) DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n", ((msg[11] & 0xc0) == 0x80)? 'R' : (msg[11] & 0x80)? 'S' : 'I', ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)), (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); rc = bulk_out (handle, msg, msglen, 0); if (rc) return rc; rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock, seqno, 5000, 0); if (rc) return rc; tpdu = msg + 10; tpdulen = msglen - 10; if (tpdulen < 4) return CCID_DRIVER_ERR_ABORTED; if (debug_level > 1) DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n", ((msg[11] & 0xc0) == 0x80)? 'R' : (msg[11] & 0x80)? 'S' : 'I', ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)), ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0, (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1) { DEBUGOUT ("invalid response for S-block (Change-IFSD)\n"); return -1; } DEBUGOUT_1 ("IFSD has been set to %d\n", tpdu[3]); } return 0; } static unsigned int compute_edc (const unsigned char *data, size_t datalen, int use_crc) { if (use_crc) { return 0x42; /* Not yet implemented. */ } else { unsigned char crc = 0; for (; datalen; datalen--) crc ^= *data++; return crc; } } /* Return true if APDU is an extended length one. */ static int is_exlen_apdu (const unsigned char *apdu, size_t apdulen) { if (apdulen < 7 || apdu[4]) return 0; /* Too short or no Z byte. */ return 1; } /* Helper for ccid_transceive used for APDU level exchanges. */ static int ccid_transceive_apdu_level (ccid_driver_t handle, const unsigned char *apdu_buf, size_t apdu_buflen, unsigned char *resp, size_t maxresplen, size_t *nresp) { int rc; unsigned char send_buffer[10+261+300], recv_buffer[10+261+300]; const unsigned char *apdu; size_t apdulen; unsigned char *msg; size_t msglen; unsigned char seqno; int bwi = 4; msg = send_buffer; apdu = apdu_buf; apdulen = apdu_buflen; assert (apdulen); /* The maximum length for a short APDU T=1 block is 261. For an extended APDU T=1 block the maximum length 65544; however extended APDU exchange level is not yet supported. */ if (apdulen > 261) return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */ msg[0] = PC_to_RDR_XfrBlock; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = bwi; /* bBWI */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ memcpy (msg+10, apdu, apdulen); set_msg_len (msg, apdulen); msglen = 10 + apdulen; rc = bulk_out (handle, msg, msglen, 0); if (rc) return rc; msg = recv_buffer; rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen, RDR_to_PC_DataBlock, seqno, 5000, 0); if (rc) return rc; apdu = msg + 10; apdulen = msglen - 10; if (resp) { if (apdulen > maxresplen) { DEBUGOUT_2 ("provided buffer too short for received data " "(%u/%u)\n", (unsigned int)apdulen, (unsigned int)maxresplen); return CCID_DRIVER_ERR_INV_VALUE; } memcpy (resp, apdu, apdulen); *nresp = apdulen; } return 0; } /* Protocol T=1 overview Block Structure: Prologue Field: 1 byte Node Address (NAD) 1 byte Protocol Control Byte (PCB) 1 byte Length (LEN) Information Field: 0-254 byte APDU or Control Information (INF) Epilogue Field: 1 byte Error Detection Code (EDC) NAD: bit 7 unused bit 4..6 Destination Node Address (DAD) bit 3 unused bit 2..0 Source Node Address (SAD) If node adresses are not used, SAD and DAD should be set to 0 on the first block sent to the card. If they are used they should have different values (0 for one is okay); that first block sets up the addresses of the nodes. PCB: Information Block (I-Block): bit 7 0 bit 6 Sequence number (yep, that is modulo 2) bit 5 Chaining flag bit 4..0 reserved Received-Ready Block (R-Block): bit 7 1 bit 6 0 bit 5 0 bit 4 Sequence number bit 3..0 0 = no error 1 = EDC or parity error 2 = other error other values are reserved Supervisory Block (S-Block): bit 7 1 bit 6 1 bit 5 clear=request,set=response bit 4..0 0 = resyncronisation request 1 = information field size request 2 = abort request 3 = extension of BWT request 4 = VPP error other values are reserved */ int ccid_transceive (ccid_driver_t handle, const unsigned char *apdu_buf, size_t apdu_buflen, unsigned char *resp, size_t maxresplen, size_t *nresp) { int rc; /* The size of the buffer used to be 10+259. For the via_escape hack we need one extra byte, thus 11+259. */ unsigned char send_buffer[11+259], recv_buffer[11+259]; const unsigned char *apdu; size_t apdulen; unsigned char *msg, *tpdu, *p; size_t msglen, tpdulen, last_tpdulen, n; unsigned char seqno; unsigned int edc; int use_crc = 0; int hdrlen, pcboff; size_t dummy_nresp; int via_escape = 0; int next_chunk = 1; int sending = 1; int retries = 0; int resyncing = 0; int nad_byte; if (!nresp) nresp = &dummy_nresp; *nresp = 0; /* Smarter readers allow to send APDUs directly; divert here. */ if (handle->apdu_level) { /* We employ a hack for Omnikey readers which are able to send TPDUs using an escape sequence. There is no documentation but the Windows driver does it this way. Tested using a CM6121. This method works also for the Cherry XX44 keyboards; however there are problems with the ccid_tranceive_secure which leads to a loss of sync on the CCID level. If Cherry wants to make their keyboard work again, they should hand over some docs. */ if ((handle->id_vendor == VENDOR_OMNIKEY || (!handle->idev && handle->id_product == TRANSPORT_CM4040)) && handle->apdu_level < 2 && is_exlen_apdu (apdu_buf, apdu_buflen)) via_escape = 1; else return ccid_transceive_apdu_level (handle, apdu_buf, apdu_buflen, resp, maxresplen, nresp); } /* The other readers we support require sending TPDUs. */ tpdulen = 0; /* Avoid compiler warning about no initialization. */ msg = send_buffer; hdrlen = via_escape? 11 : 10; /* NAD: DAD=1, SAD=0 */ nad_byte = handle->nonnull_nad? ((1 << 4) | 0): 0; if (via_escape) nad_byte = 0; last_tpdulen = 0; /* Avoid gcc warning (controlled by RESYNCING). */ for (;;) { if (next_chunk) { next_chunk = 0; apdu = apdu_buf; apdulen = apdu_buflen; assert (apdulen); /* Construct an I-Block. */ tpdu = msg + hdrlen; tpdu[0] = nad_byte; tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */ if (apdulen > handle->ifsc ) { apdulen = handle->ifsc; apdu_buf += handle->ifsc; apdu_buflen -= handle->ifsc; tpdu[1] |= (1 << 5); /* Set more bit. */ } tpdu[2] = apdulen; memcpy (tpdu+3, apdu, apdulen); tpdulen = 3 + apdulen; edc = compute_edc (tpdu, tpdulen, use_crc); if (use_crc) tpdu[tpdulen++] = (edc >> 8); tpdu[tpdulen++] = edc; } if (via_escape) { msg[0] = PC_to_RDR_Escape; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 0; /* RFU */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ msg[10] = 0x1a; /* Omnikey command to send a TPDU. */ set_msg_len (msg, 1 + tpdulen); } else { msg[0] = PC_to_RDR_XfrBlock; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 4; /* bBWI */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ set_msg_len (msg, tpdulen); } msglen = hdrlen + tpdulen; if (!resyncing) last_tpdulen = tpdulen; pcboff = hdrlen+1; if (debug_level > 1) DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n", ((msg[pcboff] & 0xc0) == 0x80)? 'R' : (msg[pcboff] & 0x80)? 'S' : 'I', ((msg[pcboff] & 0x80)? !!(msg[pcboff]& 0x10) : !!(msg[pcboff] & 0x40)), (!(msg[pcboff] & 0x80) && (msg[pcboff] & 0x20)? " [more]":"")); rc = bulk_out (handle, msg, msglen, 0); if (rc) return rc; msg = recv_buffer; rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen, via_escape? RDR_to_PC_Escape : RDR_to_PC_DataBlock, seqno, 5000, 0); if (rc) return rc; tpdu = msg + hdrlen; tpdulen = msglen - hdrlen; resyncing = 0; if (tpdulen < 4) { usb_clear_halt (handle->idev, handle->ep_bulk_in); return CCID_DRIVER_ERR_ABORTED; } if (debug_level > 1) DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n", ((msg[pcboff] & 0xc0) == 0x80)? 'R' : (msg[pcboff] & 0x80)? 'S' : 'I', ((msg[pcboff] & 0x80)? !!(msg[pcboff]& 0x10) : !!(msg[pcboff] & 0x40)), ((msg[pcboff] & 0xc0) == 0x80)? (msg[pcboff] & 0x0f) : 0, (!(msg[pcboff] & 0x80) && (msg[pcboff] & 0x20)? " [more]":"")); if (!(tpdu[1] & 0x80)) { /* This is an I-block. */ retries = 0; if (sending) { /* last block sent was successful. */ handle->t1_ns ^= 1; sending = 0; } if (!!(tpdu[1] & 0x40) != handle->t1_nr) { /* Reponse does not match our sequence number. */ msg = send_buffer; tpdu = msg + hdrlen; tpdu[0] = nad_byte; tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */ tpdu[2] = 0; tpdulen = 3; edc = compute_edc (tpdu, tpdulen, use_crc); if (use_crc) tpdu[tpdulen++] = (edc >> 8); tpdu[tpdulen++] = edc; continue; } handle->t1_nr ^= 1; p = tpdu + 3; /* Skip the prologue field. */ n = tpdulen - 3 - 1; /* Strip the epilogue field. */ /* fixme: verify the checksum. */ if (resp) { if (n > maxresplen) { DEBUGOUT_2 ("provided buffer too short for received data " "(%u/%u)\n", (unsigned int)n, (unsigned int)maxresplen); return CCID_DRIVER_ERR_INV_VALUE; } memcpy (resp, p, n); resp += n; *nresp += n; maxresplen -= n; } if (!(tpdu[1] & 0x20)) return 0; /* No chaining requested - ready. */ msg = send_buffer; tpdu = msg + hdrlen; tpdu[0] = nad_byte; tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */ tpdu[2] = 0; tpdulen = 3; edc = compute_edc (tpdu, tpdulen, use_crc); if (use_crc) tpdu[tpdulen++] = (edc >> 8); tpdu[tpdulen++] = edc; } else if ((tpdu[1] & 0xc0) == 0x80) { /* This is a R-block. */ if ( (tpdu[1] & 0x0f)) { retries++; if (via_escape && retries == 1 && (msg[pcboff] & 0x0f)) { /* Error probably due to switching to TPDU. Send a resync request. We use the recv_buffer so that we don't corrupt the send_buffer. */ msg = recv_buffer; tpdu = msg + hdrlen; tpdu[0] = nad_byte; tpdu[1] = 0xc0; /* S-block resync request. */ tpdu[2] = 0; tpdulen = 3; edc = compute_edc (tpdu, tpdulen, use_crc); if (use_crc) tpdu[tpdulen++] = (edc >> 8); tpdu[tpdulen++] = edc; resyncing = 1; DEBUGOUT ("T=1: requesting resync\n"); } else if (retries > 3) { DEBUGOUT ("T=1: 3 failed retries\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } else { /* Error: repeat last block */ msg = send_buffer; tpdulen = last_tpdulen; } } else if (sending && !!(tpdu[1] & 0x10) == handle->t1_ns) { /* Response does not match our sequence number. */ DEBUGOUT ("R-block with wrong seqno received on more bit\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } else if (sending) { /* Send next chunk. */ retries = 0; msg = send_buffer; next_chunk = 1; handle->t1_ns ^= 1; } else { DEBUGOUT ("unexpected ACK R-block received\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } } else { /* This is a S-block. */ retries = 0; DEBUGOUT_2 ("T=1: S-block %s received cmd=%d\n", (tpdu[1] & 0x20)? "response": "request", (tpdu[1] & 0x1f)); if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 1 && tpdu[2] == 1) { /* Information field size request. */ unsigned char ifsc = tpdu[3]; if (ifsc < 16 || ifsc > 254) return CCID_DRIVER_ERR_CARD_IO_ERROR; msg = send_buffer; tpdu = msg + hdrlen; tpdu[0] = nad_byte; tpdu[1] = (0xc0 | 0x20 | 1); /* S-block response */ tpdu[2] = 1; tpdu[3] = ifsc; tpdulen = 4; edc = compute_edc (tpdu, tpdulen, use_crc); if (use_crc) tpdu[tpdulen++] = (edc >> 8); tpdu[tpdulen++] = edc; DEBUGOUT_1 ("T=1: requesting an ifsc=%d\n", ifsc); } else if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2]) { /* Wait time extension request. */ unsigned char bwi = tpdu[3]; msg = send_buffer; tpdu = msg + hdrlen; tpdu[0] = nad_byte; tpdu[1] = (0xc0 | 0x20 | 3); /* S-block response */ tpdu[2] = 1; tpdu[3] = bwi; tpdulen = 4; edc = compute_edc (tpdu, tpdulen, use_crc); if (use_crc) tpdu[tpdulen++] = (edc >> 8); tpdu[tpdulen++] = edc; DEBUGOUT_1 ("T=1: waittime extension of bwi=%d\n", bwi); print_progress (handle); } else if ( (tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 0 && !tpdu[2]) { DEBUGOUT ("T=1: resync ack from reader\n"); /* Repeat previous block. */ msg = send_buffer; tpdulen = last_tpdulen; } else return CCID_DRIVER_ERR_CARD_IO_ERROR; } } /* end T=1 protocol loop. */ return 0; } /* Send the CCID Secure command to the reader. APDU_BUF should contain the APDU template. PIN_MODE defines how the pin gets formatted: 1 := The PIN is ASCII encoded and of variable length. The length of the PIN entered will be put into Lc by the reader. The APDU should me made up of 4 bytes without Lc. PINLEN_MIN and PINLEN_MAX define the limits for the pin length. 0 may be used t enable reasonable defaults. PIN_PADLEN should be 0. When called with RESP and NRESP set to NULL, the function will merely check whether the reader supports the secure command for the given APDU and PIN_MODE. */ int ccid_transceive_secure (ccid_driver_t handle, const unsigned char *apdu_buf, size_t apdu_buflen, int pin_mode, int pinlen_min, int pinlen_max, int pin_padlen, unsigned char *resp, size_t maxresplen, size_t *nresp) { int rc; unsigned char send_buffer[10+259], recv_buffer[10+259]; unsigned char *msg, *tpdu, *p; size_t msglen, tpdulen, n; unsigned char seqno; size_t dummy_nresp; int testmode; int cherry_mode = 0; testmode = !resp && !nresp; if (!nresp) nresp = &dummy_nresp; *nresp = 0; if (apdu_buflen >= 4 && apdu_buf[1] == 0x20 && (handle->has_pinpad & 1)) ; else if (apdu_buflen >= 4 && apdu_buf[1] == 0x24 && (handle->has_pinpad & 2)) return CCID_DRIVER_ERR_NOT_SUPPORTED; /* Not yet by our code. */ else return CCID_DRIVER_ERR_NO_KEYPAD; if (pin_mode != 1) return CCID_DRIVER_ERR_NOT_SUPPORTED; if (pin_padlen != 0) return CCID_DRIVER_ERR_NOT_SUPPORTED; if (!pinlen_min) pinlen_min = 1; if (!pinlen_max) pinlen_max = 25; /* Note that the 25 is the maximum value the SPR532 allows. */ if (pinlen_min < 1 || pinlen_min > 25 || pinlen_max < 1 || pinlen_max > 25 || pinlen_min > pinlen_max) return CCID_DRIVER_ERR_INV_VALUE; /* We have only tested a few readers so better don't risk anything and do not allow the use with other readers. */ switch (handle->id_vendor) { case VENDOR_SCM: /* Tested with SPR 532. */ case VENDOR_KAAN: /* Tested with KAAN Advanced (1.02). */ break; case VENDOR_CHERRY: /* The CHERRY XX44 keyboard echos an asterisk for each entered character on the keyboard channel. We use a special variant of PC_to_RDR_Secure which directs these characters to the smart card's bulk-in channel. We also need to append a zero Lc byte to the APDU. It seems that it will be replaced with the actual length instead of being appended before the APDU is send to the card. */ cherry_mode = 1; break; default: return CCID_DRIVER_ERR_NOT_SUPPORTED; } if (testmode) return 0; /* Success */ msg = send_buffer; if (handle->id_vendor == VENDOR_SCM) { DEBUGOUT ("sending escape sequence to switch to a case 1 APDU\n"); rc = send_escape_cmd (handle, (const unsigned char*)"\x80\x02\x00", 3, NULL, 0, NULL); if (rc) return rc; } msg[0] = cherry_mode? 0x89 : PC_to_RDR_Secure; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; msg[7] = 0; /* bBWI */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ msg[10] = 0; /* Perform PIN verification. */ msg[11] = 0; /* Timeout in seconds. */ msg[12] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */ if (handle->id_vendor == VENDOR_SCM) { /* For the SPR532 the next 2 bytes need to be zero. We do this for all SCM products. Kudos to Martin Paljak for this hint. */ msg[13] = msg[14] = 0; } else { msg[13] = 0x00; /* bmPINBlockString: 0 bits of pin length to insert. 0 bytes of PIN block size. */ msg[14] = 0x00; /* bmPINLengthFormat: Units are bytes, position is 0. */ } /* The following is a little endian word. */ msg[15] = pinlen_max; /* wPINMaxExtraDigit-Maximum. */ msg[16] = pinlen_min; /* wPINMaxExtraDigit-Minimum. */ msg[17] = 0x02; /* bEntryValidationCondition: Validation key pressed */ if (pinlen_min && pinlen_max && pinlen_min == pinlen_max) msg[17] |= 0x01; /* Max size reached. */ msg[18] = 0xff; /* bNumberMessage: Default. */ msg[19] = 0x04; /* wLangId-High. */ msg[20] = 0x09; /* wLangId-Low: English FIXME: use the first entry. */ msg[21] = 0; /* bMsgIndex. */ /* bTeoProlog follows: */ msg[22] = handle->nonnull_nad? ((1 << 4) | 0): 0; msg[23] = ((handle->t1_ns & 1) << 6); /* I-block */ msg[24] = 0; /* The apdulen will be filled in by the reader. */ /* APDU follows: */ msg[25] = apdu_buf[0]; /* CLA */ msg[26] = apdu_buf[1]; /* INS */ msg[27] = apdu_buf[2]; /* P1 */ msg[28] = apdu_buf[3]; /* P2 */ msglen = 29; if (cherry_mode) msg[msglen++] = 0; /* An EDC is not required. */ set_msg_len (msg, msglen - 10); rc = bulk_out (handle, msg, msglen, 0); if (rc) return rc; msg = recv_buffer; rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen, RDR_to_PC_DataBlock, seqno, 30000, 0); if (rc) return rc; tpdu = msg + 10; tpdulen = msglen - 10; if (handle->apdu_level) { if (resp) { if (tpdulen > maxresplen) { DEBUGOUT_2 ("provided buffer too short for received data " "(%u/%u)\n", (unsigned int)tpdulen, (unsigned int)maxresplen); return CCID_DRIVER_ERR_INV_VALUE; } memcpy (resp, tpdu, tpdulen); *nresp = tpdulen; } return 0; } if (tpdulen < 4) { usb_clear_halt (handle->idev, handle->ep_bulk_in); return CCID_DRIVER_ERR_ABORTED; } if (debug_level > 1) DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n", ((msg[11] & 0xc0) == 0x80)? 'R' : (msg[11] & 0x80)? 'S' : 'I', ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)), ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0, (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":"")); if (!(tpdu[1] & 0x80)) { /* This is an I-block. */ /* Last block sent was successful. */ handle->t1_ns ^= 1; if (!!(tpdu[1] & 0x40) != handle->t1_nr) { /* Reponse does not match our sequence number. */ DEBUGOUT ("I-block with wrong seqno received\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } handle->t1_nr ^= 1; p = tpdu + 3; /* Skip the prologue field. */ n = tpdulen - 3 - 1; /* Strip the epilogue field. */ /* fixme: verify the checksum. */ if (resp) { if (n > maxresplen) { DEBUGOUT_2 ("provided buffer too short for received data " "(%u/%u)\n", (unsigned int)n, (unsigned int)maxresplen); return CCID_DRIVER_ERR_INV_VALUE; } memcpy (resp, p, n); resp += n; *nresp += n; maxresplen -= n; } if (!(tpdu[1] & 0x20)) return 0; /* No chaining requested - ready. */ DEBUGOUT ("chaining requested but not supported for Secure operation\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } else if ((tpdu[1] & 0xc0) == 0x80) { /* This is a R-block. */ if ( (tpdu[1] & 0x0f)) { /* Error: repeat last block */ DEBUGOUT ("No retries supported for Secure operation\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } else if (!!(tpdu[1] & 0x10) == handle->t1_ns) { /* Reponse does not match our sequence number. */ DEBUGOUT ("R-block with wrong seqno received on more bit\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } else { /* Send next chunk. */ DEBUGOUT ("chaining not supported on Secure operation\n"); return CCID_DRIVER_ERR_CARD_IO_ERROR; } } else { /* This is a S-block. */ DEBUGOUT_2 ("T=1: S-block %s received cmd=%d for Secure operation\n", (tpdu[1] & 0x20)? "response": "request", (tpdu[1] & 0x1f)); return CCID_DRIVER_ERR_CARD_IO_ERROR; } return 0; } #ifdef TEST static void print_error (int err) { const char *p; char buf[50]; switch (err) { case 0: p = "success"; case CCID_DRIVER_ERR_OUT_OF_CORE: p = "out of core"; break; case CCID_DRIVER_ERR_INV_VALUE: p = "invalid value"; break; case CCID_DRIVER_ERR_NO_DRIVER: p = "no driver"; break; case CCID_DRIVER_ERR_NOT_SUPPORTED: p = "not supported"; break; case CCID_DRIVER_ERR_LOCKING_FAILED: p = "locking failed"; break; case CCID_DRIVER_ERR_BUSY: p = "busy"; break; case CCID_DRIVER_ERR_NO_CARD: p = "no card"; break; case CCID_DRIVER_ERR_CARD_INACTIVE: p = "card inactive"; break; case CCID_DRIVER_ERR_CARD_IO_ERROR: p = "card I/O error"; break; case CCID_DRIVER_ERR_GENERAL_ERROR: p = "general error"; break; case CCID_DRIVER_ERR_NO_READER: p = "no reader"; break; case CCID_DRIVER_ERR_ABORTED: p = "aborted"; break; default: sprintf (buf, "0x%05x", err); p = buf; break; } fprintf (stderr, "operation failed: %s\n", p); } static void print_data (const unsigned char *data, size_t length) { if (length >= 2) { fprintf (stderr, "operation status: %02X%02X\n", data[length-2], data[length-1]); length -= 2; } if (length) { fputs (" returned data:", stderr); for (; length; length--, data++) fprintf (stderr, " %02X", *data); putc ('\n', stderr); } } static void print_result (int rc, const unsigned char *data, size_t length) { if (rc) print_error (rc); else if (data) print_data (data, length); } int main (int argc, char **argv) { int rc; ccid_driver_t ccid; int slotstat; unsigned char result[512]; size_t resultlen; int no_pinpad = 0; int verify_123456 = 0; int did_verify = 0; int no_poll = 0; if (argc) { argc--; argv++; } while (argc) { if ( !strcmp (*argv, "--list")) { char *p; p = ccid_get_reader_list (); if (!p) return 1; fputs (p, stderr); free (p); return 0; } else if ( !strcmp (*argv, "--debug")) { ccid_set_debug_level (ccid_set_debug_level (-1)+1); argc--; argv++; } else if ( !strcmp (*argv, "--no-poll")) { no_poll = 1; argc--; argv++; } else if ( !strcmp (*argv, "--no-pinpad")) { no_pinpad = 1; argc--; argv++; } else if ( !strcmp (*argv, "--verify-123456")) { verify_123456 = 1; argc--; argv++; } else break; } rc = ccid_open_reader (&ccid, argc? *argv:NULL); if (rc) return 1; if (!no_poll) ccid_poll (ccid); fputs ("getting ATR ...\n", stderr); rc = ccid_get_atr (ccid, NULL, 0, NULL); if (rc) { print_error (rc); return 1; } if (!no_poll) ccid_poll (ccid); fputs ("getting slot status ...\n", stderr); rc = ccid_slot_status (ccid, &slotstat); if (rc) { print_error (rc); return 1; } if (!no_poll) ccid_poll (ccid); fputs ("selecting application OpenPGP ....\n", stderr); { static unsigned char apdu[] = { 0, 0xA4, 4, 0, 6, 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01}; rc = ccid_transceive (ccid, apdu, sizeof apdu, result, sizeof result, &resultlen); print_result (rc, result, resultlen); } if (!no_poll) ccid_poll (ccid); fputs ("getting OpenPGP DO 0x65 ....\n", stderr); { static unsigned char apdu[] = { 0, 0xCA, 0, 0x65, 254 }; rc = ccid_transceive (ccid, apdu, sizeof apdu, result, sizeof result, &resultlen); print_result (rc, result, resultlen); } if (!no_pinpad) { } if (!no_pinpad) { static unsigned char apdu[] = { 0, 0x20, 0, 0x81 }; if (ccid_transceive_secure (ccid, apdu, sizeof apdu, 1, 0, 0, 0, NULL, 0, NULL)) fputs ("can't verify using a PIN-Pad reader\n", stderr); else { fputs ("verifying CHV1 using the PINPad ....\n", stderr); rc = ccid_transceive_secure (ccid, apdu, sizeof apdu, 1, 0, 0, 0, result, sizeof result, &resultlen); print_result (rc, result, resultlen); did_verify = 1; } } if (verify_123456 && !did_verify) { fputs ("verifying that CHV1 is 123456....\n", stderr); { static unsigned char apdu[] = {0, 0x20, 0, 0x81, 6, '1','2','3','4','5','6'}; rc = ccid_transceive (ccid, apdu, sizeof apdu, result, sizeof result, &resultlen); print_result (rc, result, resultlen); } } if (!rc) { fputs ("getting OpenPGP DO 0x5E ....\n", stderr); { static unsigned char apdu[] = { 0, 0xCA, 0, 0x5E, 254 }; rc = ccid_transceive (ccid, apdu, sizeof apdu, result, sizeof result, &resultlen); print_result (rc, result, resultlen); } } ccid_close_reader (ccid); return 0; } /* * Local Variables: * compile-command: "gcc -DTEST -Wall -I/usr/local/include -lusb -g ccid-driver.c" * End: */ #endif /*TEST*/ #endif /*HAVE_LIBUSB*/ diff --git a/g10/getkey.c b/g10/getkey.c index 3c953d61e..9870710fc 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -1,3102 +1,3103 @@ /* getkey.c - Get a key from the database * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, * 2006 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 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include "util.h" #include "packet.h" #include "memory.h" #include "iobuf.h" #include "keydb.h" #include "options.h" #include "main.h" #include "trustdb.h" #include "i18n.h" #include "keyserver-internal.h" +#include "../include/host2net.h" #define MAX_PK_CACHE_ENTRIES PK_UID_CACHE_SIZE #define MAX_UID_CACHE_ENTRIES PK_UID_CACHE_SIZE #if MAX_PK_CACHE_ENTRIES < 2 #error We need the cache for key creation #endif struct getkey_ctx_s { int exact; KBNODE keyblock; KBPOS kbpos; KBNODE found_key; /* pointer into some keyblock */ int last_rc; int req_usage; int req_algo; KEYDB_HANDLE kr_handle; int not_allocated; int nitems; KEYDB_SEARCH_DESC items[1]; }; #if 0 static struct { int any; int okay_count; int nokey_count; int error_count; } lkup_stats[21]; #endif typedef struct keyid_list { struct keyid_list *next; u32 keyid[2]; } *keyid_list_t; #if MAX_PK_CACHE_ENTRIES typedef struct pk_cache_entry { struct pk_cache_entry *next; u32 keyid[2]; PKT_public_key *pk; } *pk_cache_entry_t; static pk_cache_entry_t pk_cache; static int pk_cache_entries; /* number of entries in pk cache */ static int pk_cache_disabled; #endif #if MAX_UID_CACHE_ENTRIES < 5 #error we really need the userid cache #endif typedef struct user_id_db { struct user_id_db *next; keyid_list_t keyids; int len; char name[1]; } *user_id_db_t; static user_id_db_t user_id_db; static int uid_cache_entries; /* number of entries in uid cache */ static void merge_selfsigs( KBNODE keyblock ); static int lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode ); #if 0 static void print_stats() { int i; for(i=0; i < DIM(lkup_stats); i++ ) { if( lkup_stats[i].any ) fprintf(stderr, "lookup stats: mode=%-2d ok=%-6d nokey=%-6d err=%-6d\n", i, lkup_stats[i].okay_count, lkup_stats[i].nokey_count, lkup_stats[i].error_count ); } } #endif void cache_public_key( PKT_public_key *pk ) { #if MAX_PK_CACHE_ENTRIES pk_cache_entry_t ce; u32 keyid[2]; if( pk_cache_disabled ) return; if( pk->dont_cache ) return; if( is_ELGAMAL(pk->pubkey_algo) || pk->pubkey_algo == PUBKEY_ALGO_DSA || is_RSA(pk->pubkey_algo) ) { keyid_from_pk( pk, keyid ); } else return; /* don't know how to get the keyid */ for( ce = pk_cache; ce; ce = ce->next ) if( ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1] ) { if( DBG_CACHE ) log_debug("cache_public_key: already in cache\n"); return; } if( pk_cache_entries >= MAX_PK_CACHE_ENTRIES ) { /* fixme: use another algorithm to free some cache slots */ pk_cache_disabled=1; if( opt.verbose > 1 ) log_info(_("too many entries in pk cache - disabled\n")); return; } pk_cache_entries++; ce = xmalloc( sizeof *ce ); ce->next = pk_cache; pk_cache = ce; ce->pk = copy_public_key( NULL, pk ); ce->keyid[0] = keyid[0]; ce->keyid[1] = keyid[1]; #endif } /* Return a const utf-8 string with the text "[User ID not found]". This fucntion is required so that we don't need to switch gettext's encoding temporary. */ static const char * user_id_not_found_utf8 (void) { static char *text; if (!text) text = native_to_utf8 (_("[User ID not found]")); return text; } /* * Return the user ID from the given keyblock. * We use the primary uid flag which has been set by the merge_selfsigs * function. The returned value is only valid as long as then given * keyblock is not changed */ static const char * get_primary_uid ( KBNODE keyblock, size_t *uidlen ) { KBNODE k; const char *s; for (k=keyblock; k; k=k->next ) { if ( k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data && k->pkt->pkt.user_id->is_primary ) { *uidlen = k->pkt->pkt.user_id->len; return k->pkt->pkt.user_id->name; } } s = user_id_not_found_utf8 (); *uidlen = strlen (s); return s; } static void release_keyid_list ( keyid_list_t k ) { while ( k ) { keyid_list_t k2 = k->next; xfree (k); k = k2; } } /**************** * Store the association of keyid and userid * Feed only public keys to this function. */ static void cache_user_id( KBNODE keyblock ) { user_id_db_t r; const char *uid; size_t uidlen; keyid_list_t keyids = NULL; KBNODE k; for (k=keyblock; k; k = k->next ) { if ( k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { keyid_list_t a = xmalloc_clear ( sizeof *a ); /* Hmmm: For a long list of keyids it might be an advantage * to append the keys */ keyid_from_pk( k->pkt->pkt.public_key, a->keyid ); /* first check for duplicates */ for(r=user_id_db; r; r = r->next ) { keyid_list_t b = r->keyids; for ( b = r->keyids; b; b = b->next ) { if( b->keyid[0] == a->keyid[0] && b->keyid[1] == a->keyid[1] ) { if( DBG_CACHE ) log_debug("cache_user_id: already in cache\n"); release_keyid_list ( keyids ); xfree ( a ); return; } } } /* now put it into the cache */ a->next = keyids; keyids = a; } } if ( !keyids ) BUG (); /* No key no fun */ uid = get_primary_uid ( keyblock, &uidlen ); if( uid_cache_entries >= MAX_UID_CACHE_ENTRIES ) { /* fixme: use another algorithm to free some cache slots */ r = user_id_db; user_id_db = r->next; release_keyid_list ( r->keyids ); xfree(r); uid_cache_entries--; } r = xmalloc( sizeof *r + uidlen-1 ); r->keyids = keyids; r->len = uidlen; memcpy(r->name, uid, r->len); r->next = user_id_db; user_id_db = r; uid_cache_entries++; } void getkey_disable_caches() { #if MAX_PK_CACHE_ENTRIES { pk_cache_entry_t ce, ce2; for( ce = pk_cache; ce; ce = ce2 ) { ce2 = ce->next; free_public_key( ce->pk ); xfree( ce ); } pk_cache_disabled=1; pk_cache_entries = 0; pk_cache = NULL; } #endif /* fixme: disable user id cache ? */ } static void pk_from_block ( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE keyblock ) { KBNODE a = ctx->found_key ? ctx->found_key : keyblock; assert ( a->pkt->pkttype == PKT_PUBLIC_KEY || a->pkt->pkttype == PKT_PUBLIC_SUBKEY ); copy_public_key ( pk, a->pkt->pkt.public_key ); } static void sk_from_block ( GETKEY_CTX ctx, PKT_secret_key *sk, KBNODE keyblock ) { KBNODE a = ctx->found_key ? ctx->found_key : keyblock; assert ( a->pkt->pkttype == PKT_SECRET_KEY || a->pkt->pkttype == PKT_SECRET_SUBKEY ); copy_secret_key( sk, a->pkt->pkt.secret_key); } /**************** * Get a public key and store it into the allocated pk * can be called with PK set to NULL to just read it into some * internal structures. */ int get_pubkey( PKT_public_key *pk, u32 *keyid ) { int internal = 0; int rc = 0; #if MAX_PK_CACHE_ENTRIES if(pk) { /* Try to get it from the cache. We don't do this when pk is NULL as it does not guarantee that the user IDs are cached. */ pk_cache_entry_t ce; for( ce = pk_cache; ce; ce = ce->next ) { if( ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1] ) { copy_public_key( pk, ce->pk ); return 0; } } } #endif /* more init stuff */ if( !pk ) { pk = xmalloc_clear( sizeof *pk ); internal++; } /* do a lookup */ { struct getkey_ctx_s ctx; KBNODE kb = NULL; memset( &ctx, 0, sizeof ctx ); ctx.exact = 1; /* use the key ID exactly as given */ ctx.not_allocated = 1; ctx.kr_handle = keydb_new (0); ctx.nitems = 1; ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; ctx.items[0].u.kid[0] = keyid[0]; ctx.items[0].u.kid[1] = keyid[1]; ctx.req_algo = pk->req_algo; ctx.req_usage = pk->req_usage; rc = lookup( &ctx, &kb, 0 ); if ( !rc ) { pk_from_block ( &ctx, pk, kb ); } get_pubkey_end( &ctx ); release_kbnode ( kb ); } if( !rc ) goto leave; rc = G10ERR_NO_PUBKEY; leave: if( !rc ) cache_public_key( pk ); if( internal ) free_public_key(pk); return rc; } /* Get a public key and store it into the allocated pk. This function differs from get_pubkey() in that it does not do a check of the key to avoid recursion. It should be used only in very certain cases. It will only retrieve primary keys. */ int get_pubkey_fast (PKT_public_key *pk, u32 *keyid) { int rc = 0; KEYDB_HANDLE hd; KBNODE keyblock; u32 pkid[2]; assert (pk); #if MAX_PK_CACHE_ENTRIES { /* Try to get it from the cache */ pk_cache_entry_t ce; for (ce = pk_cache; ce; ce = ce->next) { if (ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1]) { if (pk) copy_public_key (pk, ce->pk); return 0; } } } #endif hd = keydb_new (0); rc = keydb_search_kid (hd, keyid); if (rc == -1) { keydb_release (hd); return G10ERR_NO_PUBKEY; } rc = keydb_get_keyblock (hd, &keyblock); keydb_release (hd); if (rc) { log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc)); return G10ERR_NO_PUBKEY; } assert ( keyblock->pkt->pkttype == PKT_PUBLIC_KEY || keyblock->pkt->pkttype == PKT_PUBLIC_SUBKEY ); keyid_from_pk(keyblock->pkt->pkt.public_key,pkid); if(keyid[0]==pkid[0] && keyid[1]==pkid[1]) copy_public_key (pk, keyblock->pkt->pkt.public_key ); else rc=G10ERR_NO_PUBKEY; release_kbnode (keyblock); /* Not caching key here since it won't have all of the fields properly set. */ return rc; } KBNODE get_pubkeyblock( u32 *keyid ) { struct getkey_ctx_s ctx; int rc = 0; KBNODE keyblock = NULL; memset( &ctx, 0, sizeof ctx ); /* no need to set exact here because we want the entire block */ ctx.not_allocated = 1; ctx.kr_handle = keydb_new (0); ctx.nitems = 1; ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; ctx.items[0].u.kid[0] = keyid[0]; ctx.items[0].u.kid[1] = keyid[1]; rc = lookup( &ctx, &keyblock, 0 ); get_pubkey_end( &ctx ); return rc ? NULL : keyblock; } /**************** * Get a secret key and store it into sk */ int get_seckey( PKT_secret_key *sk, u32 *keyid ) { int rc; struct getkey_ctx_s ctx; KBNODE kb = NULL; memset( &ctx, 0, sizeof ctx ); ctx.exact = 1; /* use the key ID exactly as given */ ctx.not_allocated = 1; ctx.kr_handle = keydb_new (1); ctx.nitems = 1; ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; ctx.items[0].u.kid[0] = keyid[0]; ctx.items[0].u.kid[1] = keyid[1]; ctx.req_algo = sk->req_algo; ctx.req_usage = sk->req_usage; rc = lookup( &ctx, &kb, 1 ); if ( !rc ) { sk_from_block ( &ctx, sk, kb ); } get_seckey_end( &ctx ); release_kbnode ( kb ); if( !rc ) { /* check the secret key (this may prompt for a passprase to * unlock the secret key */ rc = check_secret_key( sk, 0 ); } return rc; } /**************** * Check whether the secret key is available. This is just a fast * check and does not tell us whether the secret key is valid. It * merely tells other whether there is some secret key. * Returns: 0 := key is available * G10ERR_NO_SECKEY := not availabe */ int seckey_available( u32 *keyid ) { int rc; KEYDB_HANDLE hd = keydb_new (1); rc = keydb_search_kid (hd, keyid); if ( rc == -1 ) rc = G10ERR_NO_SECKEY; keydb_release (hd); return rc; } /**************** * Return the type of the user id: * * Please use the constants KEYDB_SERCH_MODE_xxx * 0 = Invalid user ID * 1 = exact match * 2 = match a substring * 3 = match an email address * 4 = match a substring of an email address * 5 = match an email address, but compare from end * 6 = word match mode * 10 = it is a short KEYID (don't care about keyid[0]) * 11 = it is a long KEYID * 12 = it is a trustdb index (keyid is looked up) * 16 = it is a 16 byte fingerprint * 20 = it is a 20 byte fingerprint * 21 = Unified fingerprint :fpr:pk_algo: * (We don't use pk_algo yet) * * Rules used: * - If the username starts with 8,9,16 or 17 hex-digits (the first one * must be in the range 0..9), this is considered a keyid; depending * on the length a short or complete one. * - If the username starts with 32,33,40 or 41 hex-digits (the first one * must be in the range 0..9), this is considered a fingerprint. * - If the username starts with a left angle, we assume it is a complete * email address and look only at this part. * - If the username starts with a colon we assume it is a unified * key specfification. * - If the username starts with a '.', we assume it is the ending * part of an email address * - If the username starts with an '@', we assume it is a part of an * email address * - If the userid start with an '=' an exact compare is done. * - If the userid starts with a '*' a case insensitive substring search is * done (This is the default). * - If the userid starts with a '+' we will compare individual words * and a match requires that all the words are in the userid. * Words are delimited by white space or "()<>[]{}.@-+_,;/&!" * (note that you can't search for these characters). Compare * is not case sensitive. */ int classify_user_id( const char *name, KEYDB_SEARCH_DESC *desc ) { const char *s; int hexprefix = 0; int hexlength; int mode = 0; KEYDB_SEARCH_DESC dummy_desc; if (!desc) desc = &dummy_desc; /* clear the structure so that the mode field is set to zero unless * we set it to the correct value right at the end of this function */ memset (desc, 0, sizeof *desc); /* skip leading spaces. Fixme: what is with trailing spaces? */ for(s = name; *s && spacep (s); s++ ) ; switch (*s) { case 0: /* empty string is an error */ return 0; #if 0 case '.': /* an email address, compare from end */ mode = KEYDB_SEARCH_MODE_MAILEND; s++; desc->u.name = s; break; #endif case '<': /* an email address */ mode = KEYDB_SEARCH_MODE_MAIL; desc->u.name = s; break; case '@': /* part of an email address */ mode = KEYDB_SEARCH_MODE_MAILSUB; s++; desc->u.name = s; break; case '=': /* exact compare */ mode = KEYDB_SEARCH_MODE_EXACT; s++; desc->u.name = s; break; case '*': /* case insensitive substring search */ mode = KEYDB_SEARCH_MODE_SUBSTR; s++; desc->u.name = s; break; #if 0 case '+': /* compare individual words */ mode = KEYDB_SEARCH_MODE_WORDS; s++; desc->u.name = s; break; #endif case '#': /* local user id */ return 0; /* This is now obsolete and van't not be used anymore*/ case ':': /*Unified fingerprint */ { const char *se, *si; int i; se = strchr( ++s,':'); if ( !se ) return 0; for (i=0,si=s; si < se; si++, i++ ) { if ( !strchr("01234567890abcdefABCDEF", *si ) ) return 0; /* invalid digit */ } if (i != 32 && i != 40) return 0; /* invalid length of fpr*/ for (i=0,si=s; si < se; i++, si +=2) desc->u.fpr[i] = hextobyte(si); for ( ; i < 20; i++) desc->u.fpr[i]= 0; s = se + 1; mode = KEYDB_SEARCH_MODE_FPR; } break; default: if (s[0] == '0' && s[1] == 'x') { hexprefix = 1; s += 2; } hexlength = strspn(s, "0123456789abcdefABCDEF"); if (hexlength >= 8 && s[hexlength] =='!') { desc->exact = 1; hexlength++; /* just for the following check */ } /* check if a hexadecimal number is terminated by EOS or blank */ if (hexlength && s[hexlength] && !spacep(s+hexlength)) { if (hexprefix) /* a "0x" prefix without correct */ return 0; /* termination is an error */ else /* The first chars looked like */ hexlength = 0; /* a hex number, but really were not. */ } if (desc->exact) hexlength--; if (hexlength == 8 || (!hexprefix && hexlength == 9 && *s == '0')){ /* short keyid */ if (hexlength == 9) s++; desc->u.kid[0] = 0; desc->u.kid[1] = strtoul( s, NULL, 16 ); mode = KEYDB_SEARCH_MODE_SHORT_KID; } else if (hexlength == 16 || (!hexprefix && hexlength == 17 && *s == '0')) { /* complete keyid */ char buf[9]; if (hexlength == 17) s++; mem2str(buf, s, 9 ); desc->u.kid[0] = strtoul( buf, NULL, 16 ); desc->u.kid[1] = strtoul( s+8, NULL, 16 ); mode = KEYDB_SEARCH_MODE_LONG_KID; } else if (hexlength == 32 || (!hexprefix && hexlength == 33 && *s == '0')) { /* md5 fingerprint */ int i; if (hexlength == 33) s++; memset(desc->u.fpr+16, 0, 4); for (i=0; i < 16; i++, s+=2) { int c = hextobyte(s); if (c == -1) return 0; desc->u.fpr[i] = c; } mode = KEYDB_SEARCH_MODE_FPR16; } else if (hexlength == 40 || (!hexprefix && hexlength == 41 && *s == '0')) { /* sha1/rmd160 fingerprint */ int i; if (hexlength == 41) s++; for (i=0; i < 20; i++, s+=2) { int c = hextobyte(s); if (c == -1) return 0; desc->u.fpr[i] = c; } mode = KEYDB_SEARCH_MODE_FPR20; } else if (!hexprefix) { /* No hex indicator; check for a space separated OpenPGP v4 fingerprint like: 8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367 or 8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367 */ mode = 0; hexlength = strspn (s, " 0123456789abcdefABCDEF"); if (s[hexlength] && s[hexlength] != ' ') hexlength = 0; /* Followed by non-space. */ while (hexlength && s[hexlength-1] == ' ') hexlength--; /* Trim trailing spaces. */ if ((hexlength == 49 || hexlength == 50) && (!s[hexlength] || s[hexlength] == ' ')) { int i, c; for (i=0; i < 20; i++) { if (i && !(i % 2)) { if (*s != ' ') break; s++; /* Skip the double space in the middle but don't require it to help copying fingerprints from sources which fold multiple space to one. */ if (i == 10 && *s == ' ') s++; } c = hextobyte(s); if (c == -1) break; desc->u.fpr[i] = c; s += 2; } if (i == 20) mode = KEYDB_SEARCH_MODE_FPR20; } if (!mode) { desc->exact = 0; desc->u.name = s; mode = KEYDB_SEARCH_MODE_SUBSTR; /* default mode */ } } else /* This was a hex number with a prefix */ return 0; /* and a wrong length */ } desc->mode = mode; return mode; } static int skip_unusable(void *dummy, u32 *keyid,PKT_user_id *uid) { int unusable=0; KBNODE keyblock; (void)dummy; keyblock=get_pubkeyblock(keyid); if(!keyblock) { log_error("error checking usability status of %s\n",keystr(keyid)); goto leave; } /* Is the user ID in question revoked/expired? */ if(uid) { KBNODE node; for(node=keyblock;node;node=node->next) { if(node->pkt->pkttype==PKT_USER_ID) { if(cmp_user_ids(uid,node->pkt->pkt.user_id)==0 && (node->pkt->pkt.user_id->is_revoked || node->pkt->pkt.user_id->is_expired)) { unusable=1; break; } } } } if(!unusable) unusable=pk_is_disabled(keyblock->pkt->pkt.public_key); leave: release_kbnode(keyblock); return unusable; } /**************** * Try to get the pubkey by the userid. This function looks for the * first pubkey certificate which has the given name in a user_id. if * pk/sk has the pubkey algo set, the function will only return a * pubkey with that algo. If namelist is NULL, the first key is * returned. The caller should provide storage for either the pk or * the sk. If ret_kb is not NULL the function will return the * keyblock there. */ static int key_byname( GETKEY_CTX *retctx, STRLIST namelist, PKT_public_key *pk, PKT_secret_key *sk, int secmode, int include_unusable, KBNODE *ret_kb, KEYDB_HANDLE *ret_kdbhd ) { int rc = 0; int n; STRLIST r; GETKEY_CTX ctx; KBNODE help_kb = NULL; if( retctx ) {/* reset the returned context in case of error */ assert (!ret_kdbhd); /* not allowed because the handle is stored in the context */ *retctx = NULL; } if (ret_kdbhd) *ret_kdbhd = NULL; if(!namelist) { ctx = xmalloc_clear (sizeof *ctx); ctx->nitems = 1; ctx->items[0].mode=KEYDB_SEARCH_MODE_FIRST; if(!include_unusable) ctx->items[0].skipfnc=skip_unusable; } else { /* build the search context */ for(n=0, r=namelist; r; r = r->next ) n++; ctx = xmalloc_clear (sizeof *ctx + (n-1)*sizeof ctx->items ); ctx->nitems = n; for(n=0, r=namelist; r; r = r->next, n++ ) { classify_user_id (r->d, &ctx->items[n]); if (ctx->items[n].exact) ctx->exact = 1; if (!ctx->items[n].mode) { xfree (ctx); return G10ERR_INV_USER_ID; } if(!include_unusable && ctx->items[n].mode!=KEYDB_SEARCH_MODE_SHORT_KID && ctx->items[n].mode!=KEYDB_SEARCH_MODE_LONG_KID && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR16 && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR20 && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR) ctx->items[n].skipfnc=skip_unusable; } } ctx->kr_handle = keydb_new (secmode); if ( !ret_kb ) ret_kb = &help_kb; if( secmode ) { if (sk) { ctx->req_algo = sk->req_algo; ctx->req_usage = sk->req_usage; } rc = lookup( ctx, ret_kb, 1 ); if ( !rc && sk ) { sk_from_block ( ctx, sk, *ret_kb ); } } else { if (pk) { ctx->req_algo = pk->req_algo; ctx->req_usage = pk->req_usage; } rc = lookup( ctx, ret_kb, 0 ); if ( !rc && pk ) { pk_from_block ( ctx, pk, *ret_kb ); } } release_kbnode ( help_kb ); if (retctx) /* caller wants the context */ *retctx = ctx; else { if (ret_kdbhd) { *ret_kdbhd = ctx->kr_handle; ctx->kr_handle = NULL; } get_pubkey_end (ctx); } return rc; } /* Find a public key from NAME and return the keyblock or the key. If ret_kdb is not NULL, the KEYDB handle used to locate this keyblock is returned and the caller is responsible for closing it. If a key was not found and NAME is a valid RFC822 mailbox and PKA retrieval has been enabled, we try to import the pkea via the PKA mechanism. */ int get_pubkey_byname (PKT_public_key *pk, const char *name, KBNODE *ret_keyblock, KEYDB_HANDLE *ret_kdbhd, int include_unusable ) { int rc; STRLIST namelist = NULL; add_to_strlist( &namelist, name ); rc = key_byname( NULL, namelist, pk, NULL, 0, include_unusable, ret_keyblock, ret_kdbhd); /* If the requested name resembles a valid mailbox and automatic retrieval has been enabled, we try to import the key. */ if (rc == G10ERR_NO_PUBKEY && is_valid_mailbox(name)) { struct akl *akl; for(akl=opt.auto_key_locate;akl;akl=akl->next) { unsigned char *fpr=NULL; size_t fpr_len; switch(akl->type) { case AKL_CERT: glo_ctrl.in_auto_key_retrieve++; rc=keyserver_import_cert(name,&fpr,&fpr_len); glo_ctrl.in_auto_key_retrieve--; if(rc==0) log_info(_("automatically retrieved `%s' via %s\n"), name,"DNS CERT"); break; case AKL_PKA: glo_ctrl.in_auto_key_retrieve++; rc=keyserver_import_pka(name,&fpr,&fpr_len); glo_ctrl.in_auto_key_retrieve--; if(rc==0) log_info(_("automatically retrieved `%s' via %s\n"), name,"PKA"); break; case AKL_LDAP: glo_ctrl.in_auto_key_retrieve++; rc=keyserver_import_ldap(name,&fpr,&fpr_len); glo_ctrl.in_auto_key_retrieve--; if(rc==0) log_info(_("automatically retrieved `%s' via %s\n"), name,"LDAP"); break; case AKL_KEYSERVER: /* Strictly speaking, we don't need to only use a valid mailbox for the getname search, but it helps cut down on the problem of searching for something like "john" and getting a whole lot of keys back. */ if(opt.keyserver) { glo_ctrl.in_auto_key_retrieve++; rc=keyserver_import_name(name,&fpr,&fpr_len,opt.keyserver); glo_ctrl.in_auto_key_retrieve--; if(rc==0) log_info(_("automatically retrieved `%s' via %s\n"), name,opt.keyserver->uri); } break; case AKL_SPEC: { struct keyserver_spec *keyserver; keyserver=keyserver_match(akl->spec); glo_ctrl.in_auto_key_retrieve++; rc=keyserver_import_name(name,&fpr,&fpr_len,keyserver); glo_ctrl.in_auto_key_retrieve--; if(rc==0) log_info(_("automatically retrieved `%s' via %s\n"), name,akl->spec->uri); } break; } /* Use the fingerprint of the key that we actually fetched. This helps prevent problems where the key that we fetched doesn't have the same name that we used to fetch it. In the case of CERT and PKA, this is an actual security requirement as the URL might point to a key put in by an attacker. By forcing the use of the fingerprint, we won't use the attacker's key here. */ if(rc==0 && fpr) { int i; char fpr_string[MAX_FINGERPRINT_LEN*2+1]; assert(fpr_len<=MAX_FINGERPRINT_LEN); free_strlist(namelist); namelist=NULL; for(i=0;ikbpos, 0, sizeof ctx->kbpos); keydb_release (ctx->kr_handle); if( !ctx->not_allocated ) xfree( ctx ); } } /**************** * Search for a key with the given fingerprint. * FIXME: * We should replace this with the _byname function. Thiscsan be done * by creating a userID conforming to the unified fingerprint style. */ int get_pubkey_byfprint( PKT_public_key *pk, const byte *fprint, size_t fprint_len) { int rc; if( fprint_len == 20 || fprint_len == 16 ) { struct getkey_ctx_s ctx; KBNODE kb = NULL; memset( &ctx, 0, sizeof ctx ); ctx.exact = 1 ; ctx.not_allocated = 1; ctx.kr_handle = keydb_new (0); ctx.nitems = 1; ctx.items[0].mode = fprint_len==16? KEYDB_SEARCH_MODE_FPR16 : KEYDB_SEARCH_MODE_FPR20; memcpy( ctx.items[0].u.fpr, fprint, fprint_len ); rc = lookup( &ctx, &kb, 0 ); if (!rc && pk ) pk_from_block ( &ctx, pk, kb ); release_kbnode ( kb ); get_pubkey_end( &ctx ); } else rc = G10ERR_GENERAL; /* Oops */ return rc; } /* Get a public key and store it into the allocated pk. This function differs from get_pubkey_byfprint() in that it does not do a check of the key to avoid recursion. It should be used only in very certain cases. PK may be NULL to check just for the existance of the key. */ int get_pubkey_byfprint_fast (PKT_public_key *pk, const byte *fprint, size_t fprint_len) { int rc = 0; KEYDB_HANDLE hd; KBNODE keyblock; byte fprbuf[MAX_FINGERPRINT_LEN]; int i; for (i=0; i < MAX_FINGERPRINT_LEN && i < fprint_len; i++) fprbuf[i] = fprint[i]; while (i < MAX_FINGERPRINT_LEN) fprbuf[i++] = 0; hd = keydb_new (0); rc = keydb_search_fpr (hd, fprbuf); if (rc == -1) { keydb_release (hd); return G10ERR_NO_PUBKEY; } rc = keydb_get_keyblock (hd, &keyblock); keydb_release (hd); if (rc) { log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc)); return G10ERR_NO_PUBKEY; } assert ( keyblock->pkt->pkttype == PKT_PUBLIC_KEY || keyblock->pkt->pkttype == PKT_PUBLIC_SUBKEY ); if (pk) copy_public_key (pk, keyblock->pkt->pkt.public_key ); release_kbnode (keyblock); /* Not caching key here since it won't have all of the fields properly set. */ return 0; } /**************** * Search for a key with the given fingerprint and return the * complete keyblock which may have more than only this key. */ int get_keyblock_byfprint( KBNODE *ret_keyblock, const byte *fprint, size_t fprint_len ) { int rc; if( fprint_len == 20 || fprint_len == 16 ) { struct getkey_ctx_s ctx; memset( &ctx, 0, sizeof ctx ); ctx.not_allocated = 1; ctx.kr_handle = keydb_new (0); ctx.nitems = 1; ctx.items[0].mode = fprint_len==16? KEYDB_SEARCH_MODE_FPR16 : KEYDB_SEARCH_MODE_FPR20; memcpy( ctx.items[0].u.fpr, fprint, fprint_len ); rc = lookup( &ctx, ret_keyblock, 0 ); get_pubkey_end( &ctx ); } else rc = G10ERR_GENERAL; /* Oops */ return rc; } /**************** * Get a secret key by name and store it into sk * If NAME is NULL use the default key */ static int get_seckey_byname2( GETKEY_CTX *retctx, PKT_secret_key *sk, const char *name, int unprotect, KBNODE *retblock ) { STRLIST namelist = NULL; int rc,include_unusable=1; /* If we have no name, try to use the default secret key. If we have no default, we'll use the first usable one. */ if( !name && opt.def_secret_key && *opt.def_secret_key ) add_to_strlist( &namelist, opt.def_secret_key ); else if(name) add_to_strlist( &namelist, name ); else include_unusable=0; rc = key_byname( retctx, namelist, NULL, sk, 1, include_unusable, retblock, NULL ); free_strlist( namelist ); if( !rc && unprotect ) rc = check_secret_key( sk, 0 ); return rc; } int get_seckey_byname( PKT_secret_key *sk, const char *name, int unlock ) { return get_seckey_byname2 ( NULL, sk, name, unlock, NULL ); } int get_seckey_bynames( GETKEY_CTX *retctx, PKT_secret_key *sk, STRLIST names, KBNODE *ret_keyblock ) { return key_byname( retctx, names, NULL, sk, 1, 1, ret_keyblock, NULL ); } int get_seckey_next( GETKEY_CTX ctx, PKT_secret_key *sk, KBNODE *ret_keyblock ) { int rc; rc = lookup( ctx, ret_keyblock, 1 ); if ( !rc && sk && ret_keyblock ) sk_from_block ( ctx, sk, *ret_keyblock ); return rc; } void get_seckey_end( GETKEY_CTX ctx ) { get_pubkey_end( ctx ); } /**************** * Search for a key with the given fingerprint. * FIXME: * We should replace this with the _byname function. Thiscsan be done * by creating a userID conforming to the unified fingerprint style. */ int get_seckey_byfprint( PKT_secret_key *sk, const byte *fprint, size_t fprint_len) { int rc; if( fprint_len == 20 || fprint_len == 16 ) { struct getkey_ctx_s ctx; KBNODE kb = NULL; memset( &ctx, 0, sizeof ctx ); ctx.exact = 1 ; ctx.not_allocated = 1; ctx.kr_handle = keydb_new (1); ctx.nitems = 1; ctx.items[0].mode = fprint_len==16? KEYDB_SEARCH_MODE_FPR16 : KEYDB_SEARCH_MODE_FPR20; memcpy( ctx.items[0].u.fpr, fprint, fprint_len ); rc = lookup( &ctx, &kb, 1 ); if (!rc && sk ) sk_from_block ( &ctx, sk, kb ); release_kbnode ( kb ); get_seckey_end( &ctx ); } else rc = G10ERR_GENERAL; /* Oops */ return rc; } /* Search for a secret key with the given fingerprint and return the complete keyblock which may have more than only this key. */ int get_seckeyblock_byfprint (KBNODE *ret_keyblock, const byte *fprint, size_t fprint_len ) { int rc; struct getkey_ctx_s ctx; if (fprint_len != 20 && fprint_len == 16) return G10ERR_GENERAL; /* Oops */ memset (&ctx, 0, sizeof ctx); ctx.not_allocated = 1; ctx.kr_handle = keydb_new (1); ctx.nitems = 1; ctx.items[0].mode = (fprint_len==16 ? KEYDB_SEARCH_MODE_FPR16 : KEYDB_SEARCH_MODE_FPR20); memcpy (ctx.items[0].u.fpr, fprint, fprint_len); rc = lookup (&ctx, ret_keyblock, 1); get_seckey_end (&ctx); return rc; } /************************************************ ************* Merging stuff ******************** ************************************************/ /**************** * merge all selfsignatures with the keys. * FIXME: replace this at least for the public key parts * by merge_selfsigs. * It is still used in keyedit.c and * at 2 or 3 other places - check whether it is really needed. * It might be needed by the key edit and import stuff because * the keylock is changed. */ void merge_keys_and_selfsig( KBNODE keyblock ) { PKT_public_key *pk = NULL; PKT_secret_key *sk = NULL; PKT_signature *sig; KBNODE k; u32 kid[2] = { 0, 0 }; u32 sigdate = 0; if (keyblock && keyblock->pkt->pkttype == PKT_PUBLIC_KEY ) { /* divert to our new function */ merge_selfsigs (keyblock); return; } /* still need the old one because the new one can't handle secret keys */ for(k=keyblock; k; k = k->next ) { if( k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { pk = k->pkt->pkt.public_key; sk = NULL; if( pk->version < 4 ) pk = NULL; /* not needed for old keys */ else if( k->pkt->pkttype == PKT_PUBLIC_KEY ) keyid_from_pk( pk, kid ); else if( !pk->expiredate ) { /* and subkey */ /* insert the expiration date here */ /*FIXME!!! pk->expiredate = subkeys_expiretime( k, kid );*/ } sigdate = 0; } else if( k->pkt->pkttype == PKT_SECRET_KEY || k->pkt->pkttype == PKT_SECRET_SUBKEY ) { pk = NULL; sk = k->pkt->pkt.secret_key; if( sk->version < 4 ) sk = NULL; else if( k->pkt->pkttype == PKT_SECRET_KEY ) keyid_from_sk( sk, kid ); sigdate = 0; } else if( (pk || sk ) && k->pkt->pkttype == PKT_SIGNATURE && (sig=k->pkt->pkt.signature)->sig_class >= 0x10 && sig->sig_class <= 0x30 && sig->version > 3 && !(sig->sig_class == 0x18 || sig->sig_class == 0x28) && sig->keyid[0] == kid[0] && sig->keyid[1] == kid[1] ) { /* okay this is a self-signature which can be used. * This is not used for subkey binding signature, becuase this * is done above. * FIXME: We should only use this if the signature is valid * but this is time consuming - we must provide another * way to handle this */ const byte *p; u32 ed; p = parse_sig_subpkt( sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL ); if( pk ) { - ed = p? pk->timestamp + buffer_to_u32(p):0; + ed = p? pk->timestamp + buf32_to_u32(p):0; if( sig->timestamp > sigdate ) { pk->expiredate = ed; sigdate = sig->timestamp; } } else { - ed = p? sk->timestamp + buffer_to_u32(p):0; + ed = p? sk->timestamp + buf32_to_u32(p):0; if( sig->timestamp > sigdate ) { sk->expiredate = ed; sigdate = sig->timestamp; } } } if(pk && (pk->expiredate==0 || (pk->max_expiredate && pk->expiredate>pk->max_expiredate))) pk->expiredate=pk->max_expiredate; if(sk && (sk->expiredate==0 || (sk->max_expiredate && sk->expiredate>sk->max_expiredate))) sk->expiredate=sk->max_expiredate; } } static int parse_key_usage(PKT_signature *sig) { int key_usage=0; const byte *p; size_t n; byte flags; p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_KEY_FLAGS,&n); if(p && n) { /* first octet of the keyflags */ flags=*p; if(flags & 1) { key_usage |= PUBKEY_USAGE_CERT; flags&=~1; } if(flags & 2) { key_usage |= PUBKEY_USAGE_SIG; flags&=~2; } /* We do not distinguish between encrypting communications and encrypting storage. */ if(flags & (0x04|0x08)) { key_usage |= PUBKEY_USAGE_ENC; flags&=~(0x04|0x08); } if(flags & 0x20) { key_usage |= PUBKEY_USAGE_AUTH; flags&=~0x20; } if(flags) key_usage |= PUBKEY_USAGE_UNKNOWN; if (!key_usage) key_usage |= PUBKEY_USAGE_NONE; } else if (p) /* Key flags of length zero. */ key_usage |= PUBKEY_USAGE_NONE; /* We set PUBKEY_USAGE_UNKNOWN to indicate that this key has a capability that we do not handle. This serves to distinguish between a zero key usage which we handle as the default capabilities for that algorithm, and a usage that we do not handle. Likewise we use PUBKEY_USAGE_NONE to indicate that key_flags have been given but they do not specify any usage. */ return key_usage; } /* * Apply information from SIGNODE (which is the valid self-signature * associated with that UID) to the UIDNODE: * - wether the UID has been revoked * - assumed creation date of the UID * - temporary store the keyflags here * - temporary store the key expiration time here * - mark whether the primary user ID flag hat been set. * - store the preferences */ static void fixup_uidnode ( KBNODE uidnode, KBNODE signode, u32 keycreated ) { PKT_user_id *uid = uidnode->pkt->pkt.user_id; PKT_signature *sig = signode->pkt->pkt.signature; const byte *p, *sym, *hash, *zip; size_t n, nsym, nhash, nzip; sig->flags.chosen_selfsig = 1; /* we chose this one */ uid->created = 0; /* not created == invalid */ if ( IS_UID_REV ( sig ) ) { uid->is_revoked = 1; return; /* has been revoked */ } else uid->is_revoked=0; uid->expiredate = sig->expiredate; if(sig->flags.expired) { uid->is_expired = 1; return; /* has expired */ } else uid->is_expired=0; uid->created = sig->timestamp; /* this one is okay */ uid->selfsigversion = sig->version; /* If we got this far, it's not expired :) */ uid->is_expired = 0; /* store the key flags in the helper variable for later processing */ uid->help_key_usage=parse_key_usage(sig); /* ditto for the key expiration */ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); - if( p && buffer_to_u32(p) ) - uid->help_key_expire = keycreated + buffer_to_u32(p); + if( p && buf32_to_u32 (p) ) + uid->help_key_expire = keycreated + buf32_to_u32(p); else uid->help_key_expire = 0; /* Set the primary user ID flag - we will later wipe out some * of them to only have one in our keyblock */ uid->is_primary = 0; p = parse_sig_subpkt ( sig->hashed, SIGSUBPKT_PRIMARY_UID, NULL ); if ( p && *p ) uid->is_primary = 2; /* We could also query this from the unhashed area if it is not in * the hased area and then later try to decide which is the better * there should be no security problem with this. * For now we only look at the hashed one. */ /* Now build the preferences list. These must come from the hashed section so nobody can modify the ciphers a key is willing to accept. */ p = parse_sig_subpkt ( sig->hashed, SIGSUBPKT_PREF_SYM, &n ); sym = p; nsym = p?n:0; p = parse_sig_subpkt ( sig->hashed, SIGSUBPKT_PREF_HASH, &n ); hash = p; nhash = p?n:0; p = parse_sig_subpkt ( sig->hashed, SIGSUBPKT_PREF_COMPR, &n ); zip = p; nzip = p?n:0; if (uid->prefs) xfree (uid->prefs); n = nsym + nhash + nzip; if (!n) uid->prefs = NULL; else { uid->prefs = xmalloc (sizeof (*uid->prefs) * (n+1)); n = 0; for (; nsym; nsym--, n++) { uid->prefs[n].type = PREFTYPE_SYM; uid->prefs[n].value = *sym++; } for (; nhash; nhash--, n++) { uid->prefs[n].type = PREFTYPE_HASH; uid->prefs[n].value = *hash++; } for (; nzip; nzip--, n++) { uid->prefs[n].type = PREFTYPE_ZIP; uid->prefs[n].value = *zip++; } uid->prefs[n].type = PREFTYPE_NONE; /* end of list marker */ uid->prefs[n].value = 0; } /* see whether we have the MDC feature */ uid->flags.mdc = 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n); if (p && n && (p[0] & 0x01)) uid->flags.mdc = 1; /* and the keyserver modify flag */ uid->flags.ks_modify = 1; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KS_FLAGS, &n); if (p && n && (p[0] & 0x80)) uid->flags.ks_modify = 0; } static void sig_to_revoke_info(PKT_signature *sig,struct revoke_info *rinfo) { rinfo->date = sig->timestamp; rinfo->algo = sig->pubkey_algo; rinfo->keyid[0] = sig->keyid[0]; rinfo->keyid[1] = sig->keyid[1]; } static void merge_selfsigs_main(KBNODE keyblock, int *r_revoked, struct revoke_info *rinfo) { PKT_public_key *pk = NULL; KBNODE k; u32 kid[2]; u32 sigdate, uiddate, uiddate2; KBNODE signode, uidnode, uidnode2; u32 curtime = make_timestamp (); unsigned int key_usage = 0; u32 keytimestamp = 0; u32 key_expire = 0; int key_expire_seen = 0; byte sigversion = 0; *r_revoked = 0; memset(rinfo,0,sizeof(*rinfo)); if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY ) BUG (); pk = keyblock->pkt->pkt.public_key; keytimestamp = pk->timestamp; keyid_from_pk( pk, kid ); pk->main_keyid[0] = kid[0]; pk->main_keyid[1] = kid[1]; if ( pk->version < 4 ) { /* before v4 the key packet itself contains the expiration * date and there was no way to change it, so we start with * the one from the key packet */ key_expire = pk->max_expiredate; key_expire_seen = 1; } /* first pass: find the latest direct key self-signature. * We assume that the newest one overrides all others */ /* In case this key was already merged */ xfree(pk->revkey); pk->revkey=NULL; pk->numrevkeys=0; signode = NULL; sigdate = 0; /* helper to find the latest signature */ for(k=keyblock; k && k->pkt->pkttype != PKT_USER_ID; k = k->next ) { if ( k->pkt->pkttype == PKT_SIGNATURE ) { PKT_signature *sig = k->pkt->pkt.signature; if ( sig->keyid[0] == kid[0] && sig->keyid[1]==kid[1] ) { if ( check_key_signature( keyblock, k, NULL ) ) ; /* signature did not verify */ else if ( IS_KEY_REV (sig) ){ /* key has been revoked - there is no way to override * such a revocation, so we theoretically can stop now. * We should not cope with expiration times for revocations * here because we have to assume that an attacker can * generate all kinds of signatures. However due to the * fact that the key has been revoked it does not harm * either and by continuing we gather some more info on * that key. */ *r_revoked = 1; sig_to_revoke_info(sig,rinfo); } else if ( IS_KEY_SIG (sig) ) { /* Add any revocation keys onto the pk. This is particularly interesting since we normally only get data from the most recent 1F signature, but you need multiple 1F sigs to properly handle revocation keys (PGP does it this way, and a revocation key could be sensitive and hence in a different signature). */ if(sig->revkey) { int i; pk->revkey= xrealloc(pk->revkey,sizeof(struct revocation_key)* (pk->numrevkeys+sig->numrevkeys)); for(i=0;inumrevkeys;i++) memcpy(&pk->revkey[pk->numrevkeys++], sig->revkey[i], sizeof(struct revocation_key)); } if( sig->timestamp >= sigdate ) { if(sig->flags.expired) ; /* signature has expired - ignore it */ else { sigdate = sig->timestamp; signode = k; if( sig->version > sigversion ) sigversion = sig->version; } } } } } } /* Remove dupes from the revocation keys */ if(pk->revkey) { int i,j,x,changed=0; for(i=0;inumrevkeys;i++) { for(j=i+1;jnumrevkeys;j++) { if(memcmp(&pk->revkey[i],&pk->revkey[j], sizeof(struct revocation_key))==0) { /* remove j */ for(x=j;xnumrevkeys-1;x++) pk->revkey[x]=pk->revkey[x+1]; pk->numrevkeys--; j--; changed=1; } } } if(changed) pk->revkey=xrealloc(pk->revkey, pk->numrevkeys*sizeof(struct revocation_key)); } if ( signode ) { /* some information from a direct key signature take precedence * over the same information given in UID sigs. */ PKT_signature *sig = signode->pkt->pkt.signature; const byte *p; key_usage=parse_key_usage(sig); p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); - if( p && buffer_to_u32(p) ) + if( p && buf32_to_u32 (p) ) { - key_expire = keytimestamp + buffer_to_u32(p); + key_expire = keytimestamp + buf32_to_u32 (p); key_expire_seen = 1; } /* mark that key as valid: one direct key signature should * render a key as valid */ pk->is_valid = 1; } /* pass 1.5: look for key revocation signatures that were not made by the key (i.e. did a revocation key issue a revocation for us?). Only bother to do this if there is a revocation key in the first place and we're not revoked already. */ if(!*r_revoked && pk->revkey) for(k=keyblock; k && k->pkt->pkttype != PKT_USER_ID; k = k->next ) { if ( k->pkt->pkttype == PKT_SIGNATURE ) { PKT_signature *sig = k->pkt->pkt.signature; if(IS_KEY_REV(sig) && (sig->keyid[0]!=kid[0] || sig->keyid[1]!=kid[1])) { int rc=check_revocation_keys(pk,sig); if(rc==0) { *r_revoked=2; sig_to_revoke_info(sig,rinfo); /* don't continue checking since we can't be any more revoked than this */ break; } else if(rc==G10ERR_NO_PUBKEY) pk->maybe_revoked=1; /* A failure here means the sig did not verify, was not issued by a revocation key, or a revocation key loop was broken. If a revocation key isn't findable, however, the key might be revoked and we don't know it. */ /* TODO: In the future handle subkey and cert revocations? PGP doesn't, but it's in 2440. */ } } } /* second pass: look at the self-signature of all user IDs */ signode = uidnode = NULL; sigdate = 0; /* helper to find the latest signature in one user ID */ for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) { if ( k->pkt->pkttype == PKT_USER_ID ) { if ( uidnode && signode ) { fixup_uidnode ( uidnode, signode, keytimestamp ); pk->is_valid=1; } uidnode = k; signode = NULL; sigdate = 0; } else if ( k->pkt->pkttype == PKT_SIGNATURE && uidnode ) { PKT_signature *sig = k->pkt->pkt.signature; if ( sig->keyid[0] == kid[0] && sig->keyid[1]==kid[1] ) { if ( check_key_signature( keyblock, k, NULL ) ) ; /* signature did not verify */ else if ( (IS_UID_SIG (sig) || IS_UID_REV (sig)) && sig->timestamp >= sigdate ) { /* Note: we allow to invalidate cert revocations * by a newer signature. An attacker can't use this * because a key should be revoced with a key revocation. * The reason why we have to allow for that is that at * one time an email address may become invalid but later * the same email address may become valid again (hired, * fired, hired again). */ sigdate = sig->timestamp; signode = k; signode->pkt->pkt.signature->flags.chosen_selfsig=0; if( sig->version > sigversion ) sigversion = sig->version; } } } } if ( uidnode && signode ) { fixup_uidnode ( uidnode, signode, keytimestamp ); pk->is_valid = 1; } /* If the key isn't valid yet, and we have --allow-non-selfsigned-uid set, then force it valid. */ if(!pk->is_valid && opt.allow_non_selfsigned_uid) { if(opt.verbose) log_info(_("Invalid key %s made valid by" " --allow-non-selfsigned-uid\n"),keystr_from_pk(pk)); pk->is_valid = 1; } /* The key STILL isn't valid, so try and find an ultimately trusted signature. */ if(!pk->is_valid) { uidnode=NULL; for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k=k->next) { if ( k->pkt->pkttype == PKT_USER_ID ) uidnode = k; else if ( k->pkt->pkttype == PKT_SIGNATURE && uidnode ) { PKT_signature *sig = k->pkt->pkt.signature; if(sig->keyid[0] != kid[0] || sig->keyid[1]!=kid[1]) { PKT_public_key *ultimate_pk; ultimate_pk=xmalloc_clear(sizeof(*ultimate_pk)); /* We don't want to use the full get_pubkey to avoid infinite recursion in certain cases. There is no reason to check that an ultimately trusted key is still valid - if it has been revoked or the user should also renmove the ultimate trust flag. */ if(get_pubkey_fast(ultimate_pk,sig->keyid)==0 && check_key_signature2(keyblock,k,ultimate_pk, NULL,NULL,NULL,NULL)==0 && get_ownertrust(ultimate_pk)==TRUST_ULTIMATE) { free_public_key(ultimate_pk); pk->is_valid=1; break; } free_public_key(ultimate_pk); } } } } /* Record the highest selfsig version so we know if this is a v3 key through and through, or a v3 key with a v4 selfsig somewhere. This is useful in a few places to know if the key must be treated as PGP2-style or OpenPGP-style. Note that a selfsig revocation with a higher version number will also raise this value. This is okay since such a revocation must be issued by the user (i.e. it cannot be issued by someone else to modify the key behavior.) */ pk->selfsigversion=sigversion; /* Now that we had a look at all user IDs we can now get some information * from those user IDs. */ if ( !key_usage ) { /* find the latest user ID with key flags set */ uiddate = 0; /* helper to find the latest user ID */ for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) { if ( k->pkt->pkttype == PKT_USER_ID ) { PKT_user_id *uid = k->pkt->pkt.user_id; if ( uid->help_key_usage && uid->created > uiddate ) { key_usage = uid->help_key_usage; uiddate = uid->created; } } } } if ( !key_usage ) { /* no key flags at all: get it from the algo */ key_usage = openpgp_pk_algo_usage ( pk->pubkey_algo ); } else { /* check that the usage matches the usage as given by the algo */ int x = openpgp_pk_algo_usage ( pk->pubkey_algo ); if ( x ) /* mask it down to the actual allowed usage */ key_usage &= x; } /* Whatever happens, it's a primary key, so it can certify. */ pk->pubkey_usage = key_usage|PUBKEY_USAGE_CERT; if ( !key_expire_seen ) { /* find the latest valid user ID with a key expiration set * Note, that this may be a different one from the above because * some user IDs may have no expiration date set */ uiddate = 0; for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) { if ( k->pkt->pkttype == PKT_USER_ID ) { PKT_user_id *uid = k->pkt->pkt.user_id; if ( uid->help_key_expire && uid->created > uiddate ) { key_expire = uid->help_key_expire; uiddate = uid->created; } } } } /* Currently only v3 keys have a maximum expiration date, but I'll bet v5 keys get this feature again. */ if(key_expire==0 || (pk->max_expiredate && key_expire>pk->max_expiredate)) key_expire=pk->max_expiredate; pk->has_expired = key_expire >= curtime? 0 : key_expire; pk->expiredate = key_expire; /* Fixme: we should see how to get rid of the expiretime fields but * this needs changes at other places too. */ /* and now find the real primary user ID and delete all others */ uiddate = uiddate2 = 0; uidnode = uidnode2 = NULL; for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) { if ( k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data) { PKT_user_id *uid = k->pkt->pkt.user_id; if (uid->is_primary) { if(uid->created > uiddate) { uiddate = uid->created; uidnode = k; } else if(uid->created==uiddate && uidnode) { /* The dates are equal, so we need to do a different (and arbitrary) comparison. This should rarely, if ever, happen. It's good to try and guarantee that two different GnuPG users with two different keyrings at least pick the same primary. */ if(cmp_user_ids(uid,uidnode->pkt->pkt.user_id)>0) uidnode=k; } } else { if(uid->created > uiddate2) { uiddate2 = uid->created; uidnode2 = k; } else if(uid->created==uiddate2 && uidnode2) { if(cmp_user_ids(uid,uidnode2->pkt->pkt.user_id)>0) uidnode2=k; } } } } if ( uidnode ) { for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) { if ( k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data) { PKT_user_id *uid = k->pkt->pkt.user_id; if ( k != uidnode ) uid->is_primary = 0; } } } else if( uidnode2 ) { /* none is flagged primary - use the latest user ID we have, and disambiguate with the arbitrary packet comparison. */ uidnode2->pkt->pkt.user_id->is_primary = 1; } else { /* None of our uids were self-signed, so pick the one that sorts first to be the primary. This is the best we can do here since there are no self sigs to date the uids. */ uidnode = NULL; for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) { if(k->pkt->pkttype==PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data) { if(!uidnode) { uidnode=k; uidnode->pkt->pkt.user_id->is_primary=1; continue; } else { if(cmp_user_ids(k->pkt->pkt.user_id, uidnode->pkt->pkt.user_id)>0) { uidnode->pkt->pkt.user_id->is_primary=0; uidnode=k; uidnode->pkt->pkt.user_id->is_primary=1; } else k->pkt->pkt.user_id->is_primary=0; /* just to be safe */ } } } } } /* Convert a buffer to a signature. Useful for 0x19 embedded sigs. Caller must free the signature when they are done. */ static PKT_signature * buf_to_sig(const byte *buf,size_t len) { PKT_signature *sig=xmalloc_clear(sizeof(PKT_signature)); IOBUF iobuf=iobuf_temp_with_content(buf,len); int save_mode=set_packet_list_mode(0); if(parse_signature(iobuf,PKT_SIGNATURE,len,sig)!=0) { xfree(sig); sig=NULL; } set_packet_list_mode(save_mode); iobuf_close(iobuf); return sig; } static void merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode ) { PKT_public_key *mainpk = NULL, *subpk = NULL; PKT_signature *sig; KBNODE k; u32 mainkid[2]; u32 sigdate = 0; KBNODE signode; u32 curtime = make_timestamp (); unsigned int key_usage = 0; u32 keytimestamp = 0; u32 key_expire = 0; const byte *p; if ( subnode->pkt->pkttype != PKT_PUBLIC_SUBKEY ) BUG (); mainpk = keyblock->pkt->pkt.public_key; if ( mainpk->version < 4 ) return; /* (actually this should never happen) */ keyid_from_pk( mainpk, mainkid ); subpk = subnode->pkt->pkt.public_key; keytimestamp = subpk->timestamp; subpk->is_valid = 0; subpk->main_keyid[0] = mainpk->main_keyid[0]; subpk->main_keyid[1] = mainpk->main_keyid[1]; /* find the latest key binding self-signature. */ signode = NULL; sigdate = 0; /* helper to find the latest signature */ for(k=subnode->next; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) { if ( k->pkt->pkttype == PKT_SIGNATURE ) { sig = k->pkt->pkt.signature; if ( sig->keyid[0] == mainkid[0] && sig->keyid[1]==mainkid[1] ) { if ( check_key_signature( keyblock, k, NULL ) ) ; /* signature did not verify */ else if ( IS_SUBKEY_REV (sig) ) { /* Note that this means that the date on a revocation sig does not matter - even if the binding sig is dated after the revocation sig, the subkey is still marked as revoked. This seems ok, as it is just as easy to make new subkeys rather than re-sign old ones as the problem is in the distribution. Plus, PGP (7) does this the same way. */ subpk->is_revoked = 1; sig_to_revoke_info(sig,&subpk->revoked); /* although we could stop now, we continue to * figure out other information like the old expiration * time */ } else if ( IS_SUBKEY_SIG (sig) && sig->timestamp >= sigdate ) { if(sig->flags.expired) ; /* signature has expired - ignore it */ else { sigdate = sig->timestamp; signode = k; signode->pkt->pkt.signature->flags.chosen_selfsig=0; } } } } } /* no valid key binding */ if ( !signode ) return; sig = signode->pkt->pkt.signature; sig->flags.chosen_selfsig=1; /* so we know which selfsig we chose later */ key_usage=parse_key_usage(sig); if ( !key_usage ) { /* no key flags at all: get it from the algo */ key_usage = openpgp_pk_algo_usage ( subpk->pubkey_algo ); } else { /* check that the usage matches the usage as given by the algo */ int x = openpgp_pk_algo_usage ( subpk->pubkey_algo ); if ( x ) /* mask it down to the actual allowed usage */ key_usage &= x; } subpk->pubkey_usage = key_usage; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); - if ( p && buffer_to_u32(p) ) - key_expire = keytimestamp + buffer_to_u32(p); + if ( p && buf32_to_u32 (p) ) + key_expire = keytimestamp + buf32_to_u32 (p); else key_expire = 0; subpk->has_expired = key_expire >= curtime? 0 : key_expire; subpk->expiredate = key_expire; /* Check that algo exists. Elgamal sign+encrypt are only allowed with option --rfc2440. */ if (RFC2440 && subpk->pubkey_algo == PUBKEY_ALGO_ELGAMAL) ; else if(check_pubkey_algo(subpk->pubkey_algo)) return; subpk->is_valid = 1; /* Find the most recent 0x19 embedded signature on our self-sig. */ if(subpk->backsig==0) { int seq=0; size_t n; PKT_signature *backsig=NULL; sigdate=0; /* We do this while() since there may be other embedded signatures in the future. We only want 0x19 here. */ while((p=enum_sig_subpkt(sig->hashed, SIGSUBPKT_SIGNATURE,&n,&seq,NULL))) if(n>3 && ((p[0]==3 && p[2]==0x19) || (p[0]==4 && p[1]==0x19))) { PKT_signature *tempsig=buf_to_sig(p,n); if(tempsig) { if(tempsig->timestamp>sigdate) { if(backsig) free_seckey_enc(backsig); backsig=tempsig; sigdate=backsig->timestamp; } else free_seckey_enc(tempsig); } } seq=0; /* It is safe to have this in the unhashed area since the 0x19 is located on the selfsig for convenience, not security. */ while((p=enum_sig_subpkt(sig->unhashed,SIGSUBPKT_SIGNATURE, &n,&seq,NULL))) if(n>3 && ((p[0]==3 && p[2]==0x19) || (p[0]==4 && p[1]==0x19))) { PKT_signature *tempsig=buf_to_sig(p,n); if(tempsig) { if(tempsig->timestamp>sigdate) { if(backsig) free_seckey_enc(backsig); backsig=tempsig; sigdate=backsig->timestamp; } else free_seckey_enc(tempsig); } } if(backsig) { /* At ths point, backsig contains the most recent 0x19 sig. Let's see if it is good. */ /* 2==valid, 1==invalid, 0==didn't check */ if(check_backsig(mainpk,subpk,backsig)==0) subpk->backsig=2; else subpk->backsig=1; free_seckey_enc(backsig); } } } /* * Merge information from the self-signatures with the key, so that * we can later use them more easy. * The function works by first applying the self signatures to the * primary key and the to each subkey. * Here are the rules we use to decide which inormation from which * self-signature is used: * We check all self signatures or validity and ignore all invalid signatures. * All signatures are then ordered by their creation date .... * For the primary key: * FIXME the docs */ static void merge_selfsigs( KBNODE keyblock ) { KBNODE k; int revoked; struct revoke_info rinfo; PKT_public_key *main_pk; prefitem_t *prefs; int mdc_feature; if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY ) { if (keyblock->pkt->pkttype == PKT_SECRET_KEY ) { log_error ("expected public key but found secret key " "- must stop\n"); /* we better exit here becuase a public key is expected at other places too. FIXME: Figure this out earlier and don't get to here at all */ g10_exit (1); } BUG (); } merge_selfsigs_main ( keyblock, &revoked, &rinfo ); /* now merge in the data from each of the subkeys */ for(k=keyblock; k; k = k->next ) { if ( k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { merge_selfsigs_subkey ( keyblock, k ); } } main_pk = keyblock->pkt->pkt.public_key; if ( revoked || main_pk->has_expired || !main_pk->is_valid ) { /* if the primary key is revoked, expired, or invalid we * better set the appropriate flags on that key and all * subkeys */ for(k=keyblock; k; k = k->next ) { if ( k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { PKT_public_key *pk = k->pkt->pkt.public_key; if(!main_pk->is_valid) pk->is_valid = 0; if(revoked && !pk->is_revoked) { pk->is_revoked = revoked; memcpy(&pk->revoked,&rinfo,sizeof(rinfo)); } if(main_pk->has_expired) pk->has_expired = main_pk->has_expired; } } return; } /* set the preference list of all keys to those of the primary real * user ID. Note: we use these preferences when we don't know by * which user ID the key has been selected. * fixme: we should keep atoms of commonly used preferences or * use reference counting to optimize the preference lists storage. * FIXME: it might be better to use the intersection of * all preferences. * Do a similar thing for the MDC feature flag. */ prefs = NULL; mdc_feature = 0; for (k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data && k->pkt->pkt.user_id->is_primary) { prefs = k->pkt->pkt.user_id->prefs; mdc_feature = k->pkt->pkt.user_id->flags.mdc; break; } } for(k=keyblock; k; k = k->next ) { if ( k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { PKT_public_key *pk = k->pkt->pkt.public_key; if (pk->prefs) xfree (pk->prefs); pk->prefs = copy_prefs (prefs); pk->mdc_feature = mdc_feature; } } } /* * Merge the secret keys from secblock into the pubblock thereby * replacing the public (sub)keys with their secret counterparts Hmmm: * It might be better to get away from the concept of entire secret * keys at all and have a way to store just the real secret parts * from the key. */ static void merge_public_with_secret ( KBNODE pubblock, KBNODE secblock ) { KBNODE pub; assert ( pubblock->pkt->pkttype == PKT_PUBLIC_KEY ); assert ( secblock->pkt->pkttype == PKT_SECRET_KEY ); for (pub=pubblock; pub; pub = pub->next ) { if ( pub->pkt->pkttype == PKT_PUBLIC_KEY ) { PKT_public_key *pk = pub->pkt->pkt.public_key; PKT_secret_key *sk = secblock->pkt->pkt.secret_key; assert ( pub == pubblock ); /* only in the first node */ /* there is nothing to compare in this case, so just replace * some information */ copy_public_parts_to_secret_key ( pk, sk ); free_public_key ( pk ); pub->pkt->pkttype = PKT_SECRET_KEY; pub->pkt->pkt.secret_key = copy_secret_key (NULL, sk); } else if ( pub->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { KBNODE sec; PKT_public_key *pk = pub->pkt->pkt.public_key; /* this is more complicated: it may happen that the sequence * of the subkeys dosn't match, so we have to find the * appropriate secret key */ for (sec=secblock->next; sec; sec = sec->next ) { if ( sec->pkt->pkttype == PKT_SECRET_SUBKEY ) { PKT_secret_key *sk = sec->pkt->pkt.secret_key; if ( !cmp_public_secret_key ( pk, sk ) ) { copy_public_parts_to_secret_key ( pk, sk ); free_public_key ( pk ); pub->pkt->pkttype = PKT_SECRET_SUBKEY; pub->pkt->pkt.secret_key = copy_secret_key (NULL, sk); break; } } } if ( !sec ) BUG(); /* already checked in premerge */ } } } /* This function checks that for every public subkey a corresponding * secret subkey is available and deletes the public subkey otherwise. * We need this function because we can't delete it later when we * actually merge the secret parts into the pubring. * The function also plays some games with the node flags. */ static void premerge_public_with_secret ( KBNODE pubblock, KBNODE secblock ) { KBNODE last, pub; assert ( pubblock->pkt->pkttype == PKT_PUBLIC_KEY ); assert ( secblock->pkt->pkttype == PKT_SECRET_KEY ); for (pub=pubblock,last=NULL; pub; last = pub, pub = pub->next ) { pub->flag &= ~3; /* reset bits 0 and 1 */ if ( pub->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { KBNODE sec; PKT_public_key *pk = pub->pkt->pkt.public_key; for (sec=secblock->next; sec; sec = sec->next ) { if ( sec->pkt->pkttype == PKT_SECRET_SUBKEY ) { PKT_secret_key *sk = sec->pkt->pkt.secret_key; if ( !cmp_public_secret_key ( pk, sk ) ) { if ( sk->protect.s2k.mode == 1001 ) { /* The secret parts are not available so we can't use that key for signing etc. Fix the pubkey usage */ pk->pubkey_usage &= ~(PUBKEY_USAGE_SIG |PUBKEY_USAGE_AUTH); } /* transfer flag bits 0 and 1 to the pubblock */ pub->flag |= (sec->flag &3); break; } } } if ( !sec ) { KBNODE next, ll; if (opt.verbose) log_info (_("no secret subkey" " for public subkey %s - ignoring\n"), keystr_from_pk (pk)); /* we have to remove the subkey in this case */ assert ( last ); /* find the next subkey */ for (next=pub->next,ll=pub; next && next->pkt->pkttype != PKT_PUBLIC_SUBKEY; ll = next, next = next->next ) ; /* make new link */ last->next = next; /* release this public subkey with all sigs */ ll->next = NULL; release_kbnode( pub ); /* let the loop continue */ pub = last; } } } /* We need to copy the found bits (0 and 1) from the secret key to the public key. This has already been done for the subkeys but got lost on the primary key - fix it here *. */ pubblock->flag |= (secblock->flag & 3); } /* See see whether the key fits * our requirements and in case we do not * request the primary key, we should select * a suitable subkey. * FIXME: Check against PGP 7 whether we still need a kludge * to favor type 16 keys over type 20 keys when type 20 * has not been explitely requested. * Returns: True when a suitable key has been found. * * We have to distinguish four cases: FIXME! * 1. No usage and no primary key requested * Examples for this case are that we have a keyID to be used * for decrytion or verification. * 2. No usage but primary key requested * This is the case for all functions which work on an * entire keyblock, e.g. for editing or listing * 3. Usage and primary key requested * FXME * 4. Usage but no primary key requested * FIXME * FIXME: Tell what is going to happen here and something about the rationale * Note: We don't use this function if no specific usage is requested; * This way the getkey functions can be used for plain key listings. * * CTX ist the keyblock we are investigating, if FOUNDK is not NULL this * is the key we actually found by looking at the keyid or a fingerprint and * may eitehr point to the primary or one of the subkeys. */ static int finish_lookup (GETKEY_CTX ctx) { KBNODE keyblock = ctx->keyblock; KBNODE k; KBNODE foundk = NULL; PKT_user_id *foundu = NULL; #define USAGE_MASK (PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC|PUBKEY_USAGE_CERT) unsigned int req_usage = ( ctx->req_usage & USAGE_MASK ); /* Request the primary if we're certifying another key, and also if signing data while --pgp6 or --pgp7 is on since pgp 6 and 7 do not understand signatures made by a signing subkey. PGP 8 does. */ int req_prim = (ctx->req_usage & PUBKEY_USAGE_CERT) || ((PGP6 || PGP7) && (ctx->req_usage & PUBKEY_USAGE_SIG)); u32 latest_date; KBNODE latest_key; u32 curtime = make_timestamp (); assert( keyblock->pkt->pkttype == PKT_PUBLIC_KEY ); ctx->found_key = NULL; if (ctx->exact) { for (k=keyblock; k; k = k->next) { if ( (k->flag & 1) ) { assert ( k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ); foundk = k; break; } } } for (k=keyblock; k; k = k->next) { if ( (k->flag & 2) ) { assert (k->pkt->pkttype == PKT_USER_ID); foundu = k->pkt->pkt.user_id; break; } } if ( DBG_CACHE ) log_debug( "finish_lookup: checking key %08lX (%s)(req_usage=%x)\n", (ulong)keyid_from_pk( keyblock->pkt->pkt.public_key, NULL), foundk? "one":"all", req_usage); if (!req_usage) { latest_key = foundk? foundk:keyblock; goto found; } latest_date = 0; latest_key = NULL; /* do not look at subkeys if a certification key is requested */ if ((!foundk || foundk->pkt->pkttype == PKT_PUBLIC_SUBKEY) && !req_prim) { KBNODE nextk; /* either start a loop or check just this one subkey */ for (k=foundk?foundk:keyblock; k; k = nextk ) { PKT_public_key *pk; nextk = k->next; if ( k->pkt->pkttype != PKT_PUBLIC_SUBKEY ) continue; if ( foundk ) nextk = NULL; /* what a hack */ pk = k->pkt->pkt.public_key; if (DBG_CACHE) log_debug( "\tchecking subkey %08lX\n", (ulong)keyid_from_pk( pk, NULL)); if ( !pk->is_valid ) { if (DBG_CACHE) log_debug( "\tsubkey not valid\n"); continue; } if ( pk->is_revoked ) { if (DBG_CACHE) log_debug( "\tsubkey has been revoked\n"); continue; } if ( pk->has_expired ) { if (DBG_CACHE) log_debug( "\tsubkey has expired\n"); continue; } if ( pk->timestamp > curtime && !opt.ignore_valid_from ) { if (DBG_CACHE) log_debug( "\tsubkey not yet valid\n"); continue; } if ( !((pk->pubkey_usage&USAGE_MASK) & req_usage) ) { if (DBG_CACHE) log_debug( "\tusage does not match: want=%x have=%x\n", req_usage, pk->pubkey_usage ); continue; } if (DBG_CACHE) log_debug( "\tsubkey looks fine\n"); if ( pk->timestamp > latest_date ) { latest_date = pk->timestamp; latest_key = k; } } } /* Okay now try the primary key unless we want an exact * key ID match on a subkey */ if ((!latest_key && !(ctx->exact && foundk != keyblock)) || req_prim) { PKT_public_key *pk; if (DBG_CACHE && !foundk && !req_prim ) log_debug( "\tno suitable subkeys found - trying primary\n"); pk = keyblock->pkt->pkt.public_key; if ( !pk->is_valid ) { if (DBG_CACHE) log_debug( "\tprimary key not valid\n"); } else if ( pk->is_revoked ) { if (DBG_CACHE) log_debug( "\tprimary key has been revoked\n"); } else if ( pk->has_expired ) { if (DBG_CACHE) log_debug( "\tprimary key has expired\n"); } else if ( !((pk->pubkey_usage&USAGE_MASK) & req_usage) ) { if (DBG_CACHE) log_debug( "\tprimary key usage does not match: " "want=%x have=%x\n", req_usage, pk->pubkey_usage ); } else { /* okay */ if (DBG_CACHE) log_debug( "\tprimary key may be used\n"); latest_key = keyblock; latest_date = pk->timestamp; } } if ( !latest_key ) { if (DBG_CACHE) log_debug("\tno suitable key found - giving up\n"); return 0; } found: if (DBG_CACHE) log_debug( "\tusing key %08lX\n", (ulong)keyid_from_pk( latest_key->pkt->pkt.public_key, NULL) ); if (latest_key) { PKT_public_key *pk = latest_key->pkt->pkt.public_key; if (pk->user_id) free_user_id (pk->user_id); pk->user_id = scopy_user_id (foundu); } ctx->found_key = latest_key; if (latest_key != keyblock && opt.verbose) { char *tempkeystr= xstrdup(keystr_from_pk(latest_key->pkt->pkt.public_key)); log_info(_("using subkey %s instead of primary key %s\n"), tempkeystr, keystr_from_pk(keyblock->pkt->pkt.public_key)); xfree(tempkeystr); } cache_user_id( keyblock ); return 1; /* found */ } static int lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode ) { int rc; KBNODE secblock = NULL; /* helper */ int no_suitable_key = 0; rc = 0; while (!(rc = keydb_search (ctx->kr_handle, ctx->items, ctx->nitems))) { /* If we are searching for the first key we have to make sure that the next interation does not no an implicit reset. This can be triggered by an empty key ring. */ if (ctx->nitems && ctx->items->mode == KEYDB_SEARCH_MODE_FIRST) ctx->items->mode = KEYDB_SEARCH_MODE_NEXT; rc = keydb_get_keyblock (ctx->kr_handle, &ctx->keyblock); if (rc) { log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc)); rc = 0; goto skip; } if ( secmode ) { /* find the correspondig public key and use this * this one for the selection process */ u32 aki[2]; KBNODE k = ctx->keyblock; if (k->pkt->pkttype != PKT_SECRET_KEY) BUG(); keyid_from_sk (k->pkt->pkt.secret_key, aki); k = get_pubkeyblock (aki); if( !k ) { if (!opt.quiet) log_info(_("key %s: secret key without public key" " - skipped\n"), keystr(aki)); goto skip; } secblock = ctx->keyblock; ctx->keyblock = k; premerge_public_with_secret ( ctx->keyblock, secblock ); } /* warning: node flag bits 0 and 1 should be preserved by * merge_selfsigs. For secret keys, premerge did tranfer the * keys to the keyblock */ merge_selfsigs ( ctx->keyblock ); if ( finish_lookup (ctx) ) { no_suitable_key = 0; if ( secmode ) { merge_public_with_secret ( ctx->keyblock, secblock); release_kbnode (secblock); secblock = NULL; } goto found; } else no_suitable_key = 1; skip: /* release resources and continue search */ if ( secmode ) { release_kbnode( secblock ); secblock = NULL; } release_kbnode( ctx->keyblock ); ctx->keyblock = NULL; } found: if( rc && rc != -1 ) log_error("keydb_search failed: %s\n", g10_errstr(rc)); if( !rc ) { *ret_keyblock = ctx->keyblock; /* return the keyblock */ ctx->keyblock = NULL; } else if (rc == -1 && no_suitable_key) rc = secmode ? G10ERR_UNU_SECKEY : G10ERR_UNU_PUBKEY; else if( rc == -1 ) rc = secmode ? G10ERR_NO_SECKEY : G10ERR_NO_PUBKEY; if ( secmode ) { release_kbnode( secblock ); secblock = NULL; } release_kbnode( ctx->keyblock ); ctx->keyblock = NULL; ctx->last_rc = rc; return rc; } /**************** * FIXME: Replace by the generic function * It does not work as it is right now - it is used at * 2 places: a) to get the key for an anonyous recipient * b) to get the ultimately trusted keys. * The a) usage might have some problems. * * set with_subkeys true to include subkeys * set with_spm true to include secret-parts-missing keys * * Enumerate all primary secret keys. Caller must use these procedure: * 1) create a void pointer and initialize it to NULL * 2) pass this void pointer by reference to this function * and provide space for the secret key (pass a buffer for sk) * 3) call this function as long as it does not return -1 * to indicate EOF. * 4) Always call this function a last time with SK set to NULL, * so that can free it's context. */ int enum_secret_keys( void **context, PKT_secret_key *sk, int with_subkeys, int with_spm ) { int rc=0; struct { int eof; int first; KEYDB_HANDLE hd; KBNODE keyblock; KBNODE node; } *c = *context; if( !c ) { /* make a new context */ c = xmalloc_clear( sizeof *c ); *context = c; c->hd = keydb_new (1); c->first = 1; c->keyblock = NULL; c->node = NULL; } if( !sk ) { /* free the context */ keydb_release (c->hd); release_kbnode (c->keyblock); xfree( c ); *context = NULL; return 0; } if( c->eof ) return -1; do { /* get the next secret key from the current keyblock */ for (; c->node; c->node = c->node->next) { if ((c->node->pkt->pkttype == PKT_SECRET_KEY || (with_subkeys && c->node->pkt->pkttype == PKT_SECRET_SUBKEY) ) && !(c->node->pkt->pkt.secret_key->protect.s2k.mode==1001 && !with_spm)) { copy_secret_key (sk, c->node->pkt->pkt.secret_key ); c->node = c->node->next; return 0; /* found */ } } release_kbnode (c->keyblock); c->keyblock = c->node = NULL; rc = c->first? keydb_search_first (c->hd) : keydb_search_next (c->hd); c->first = 0; if (rc) { keydb_release (c->hd); c->hd = NULL; c->eof = 1; return -1; /* eof */ } rc = keydb_get_keyblock (c->hd, &c->keyblock); c->node = c->keyblock; } while (!rc); return rc; /* error */ } /********************************************* *********** user ID printing helpers ******* *********************************************/ /**************** * Return a string with a printable representation of the user_id. * this string must be freed by xfree. */ char* get_user_id_string( u32 *keyid ) { user_id_db_t r; char *p; int pass=0; /* try it two times; second pass reads from key resources */ do { for(r=user_id_db; r; r = r->next ) { keyid_list_t a; for (a=r->keyids; a; a= a->next ) { if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) { p = xmalloc( keystrlen() + 1 + r->len + 1 ); sprintf(p, "%s %.*s", keystr(keyid), r->len, r->name ); return p; } } } } while( ++pass < 2 && !get_pubkey( NULL, keyid ) ); p = xmalloc( keystrlen() + 5 ); sprintf(p, "%s [?]", keystr(keyid)); return p; } char* get_user_id_string_native ( u32 *keyid ) { char *p = get_user_id_string( keyid ); char *p2 = utf8_to_native( p, strlen(p), 0 ); xfree(p); return p2; } char* get_long_user_id_string( u32 *keyid ) { user_id_db_t r; char *p; int pass=0; /* try it two times; second pass reads from key resources */ do { for(r=user_id_db; r; r = r->next ) { keyid_list_t a; for (a=r->keyids; a; a= a->next ) { if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) { p = xmalloc( r->len + 20 ); sprintf(p, "%08lX%08lX %.*s", (ulong)keyid[0], (ulong)keyid[1], r->len, r->name ); return p; } } } } while( ++pass < 2 && !get_pubkey( NULL, keyid ) ); p = xmalloc( 25 ); sprintf(p, "%08lX%08lX [?]", (ulong)keyid[0], (ulong)keyid[1] ); return p; } char* get_user_id( u32 *keyid, size_t *rn ) { user_id_db_t r; char *p; int pass=0; /* try it two times; second pass reads from key resources */ do { for(r=user_id_db; r; r = r->next ) { keyid_list_t a; for (a=r->keyids; a; a= a->next ) { if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) { p = xmalloc( r->len ); memcpy(p, r->name, r->len ); *rn = r->len; return p; } } } } while( ++pass < 2 && !get_pubkey( NULL, keyid ) ); p = xstrdup( user_id_not_found_utf8 () ); *rn = strlen(p); return p; } char* get_user_id_native( u32 *keyid ) { size_t rn; char *p = get_user_id( keyid, &rn ); char *p2 = utf8_to_native( p, rn, 0 ); xfree(p); return p2; } KEYDB_HANDLE get_ctx_handle(GETKEY_CTX ctx) { return ctx->kr_handle; } static void free_akl(struct akl *akl) { if(akl->spec) free_keyserver_spec(akl->spec); xfree(akl); } void release_akl(void) { while(opt.auto_key_locate) { struct akl *akl2=opt.auto_key_locate; opt.auto_key_locate=opt.auto_key_locate->next; free_akl(akl2); } } int parse_auto_key_locate(char *options) { char *tok; while((tok=optsep(&options))) { struct akl *akl,*check,*last=NULL; int dupe=0; if(tok[0]=='\0') continue; /* For now we silently ignore the new methods introduced with 2.0.10. */ if (!ascii_strcasecmp (tok,"nodefault") || !ascii_strcasecmp (tok,"local")) continue; akl=xmalloc_clear(sizeof(*akl)); if(ascii_strcasecmp(tok,"ldap")==0) akl->type=AKL_LDAP; else if(ascii_strcasecmp(tok,"keyserver")==0) akl->type=AKL_KEYSERVER; #ifdef USE_DNS_CERT else if(ascii_strcasecmp(tok,"cert")==0) akl->type=AKL_CERT; #endif #ifdef USE_DNS_PKA else if(ascii_strcasecmp(tok,"pka")==0) akl->type=AKL_PKA; #endif else if((akl->spec=parse_keyserver_uri(tok,1,NULL,0))) akl->type=AKL_SPEC; else { free_akl(akl); return 0; } /* We must maintain the order the user gave us */ for(check=opt.auto_key_locate;check;last=check,check=check->next) { /* Check for duplicates */ if(check->type==akl->type && (akl->type!=AKL_SPEC || (akl->type==AKL_SPEC && strcmp(check->spec->uri,akl->spec->uri)==0))) { dupe=1; free_akl(akl); break; } } if(!dupe) { if(last) last->next=akl; else opt.auto_key_locate=akl; } } return 1; } diff --git a/g10/keygen.c b/g10/keygen.c index 995ba6389..76ee74e63 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1,3947 +1,3945 @@ /* keygen.c - generate a key pair * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * 2007, 2009 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 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include #include #include "util.h" #include "main.h" #include "packet.h" #include "cipher.h" #include "ttyio.h" #include "options.h" #include "keydb.h" #include "trustdb.h" #include "status.h" #include "i18n.h" #include "cardglue.h" #include "keyserver-internal.h" +#include "host2net.h" #define MAX_PREFS 30 enum para_name { pKEYTYPE, pKEYLENGTH, pKEYUSAGE, pSUBKEYTYPE, pSUBKEYLENGTH, pSUBKEYUSAGE, pAUTHKEYTYPE, pNAMEREAL, pNAMEEMAIL, pNAMECOMMENT, pPREFERENCES, pREVOKER, pUSERID, pCREATIONDATE, pKEYCREATIONDATE, /* Same in seconds since epoch. */ pEXPIREDATE, pKEYEXPIRE, /* in n seconds */ pSUBKEYEXPIRE, /* in n seconds */ pPASSPHRASE, pPASSPHRASE_DEK, pPASSPHRASE_S2K, pSERIALNO, pBACKUPENCDIR, pHANDLE, pKEYSERVER }; struct para_data_s { struct para_data_s *next; int lnr; enum para_name key; union { DEK *dek; STRING2KEY *s2k; u32 expire; u32 creation; unsigned int usage; struct revocation_key revkey; char value[1]; } u; }; struct output_control_s { int lnr; int dryrun; int use_files; struct { char *fname; char *newfname; IOBUF stream; armor_filter_context_t afx; } pub; struct { char *fname; char *newfname; IOBUF stream; armor_filter_context_t afx; } sec; }; struct opaque_data_usage_and_pk { unsigned int usage; PKT_public_key *pk; }; static int prefs_initialized = 0; static byte sym_prefs[MAX_PREFS]; static int nsym_prefs; static byte hash_prefs[MAX_PREFS]; static int nhash_prefs; static byte zip_prefs[MAX_PREFS]; static int nzip_prefs; static int mdc_available,ks_modify; static void do_generate_keypair (struct para_data_s *para, struct output_control_s *outctrl, int card); static int write_keyblock( IOBUF out, KBNODE node ); static int gen_card_key (int algo, int keyno, int is_primary, KBNODE pub_root, KBNODE sec_root, PKT_secret_key **ret_sk, u32 *timestamp, u32 expireval, struct para_data_s *para); static int gen_card_key_with_backup (int algo, int keyno, int is_primary, KBNODE pub_root, KBNODE sec_root, u32 timestamp, u32 expireval, struct para_data_s *para, const char *backup_dir); static void print_status_key_created (int letter, PKT_public_key *pk, const char *handle) { byte array[MAX_FINGERPRINT_LEN], *s; char *buf, *p; size_t i, n; if (!handle) handle = ""; buf = xmalloc (MAX_FINGERPRINT_LEN*2+31 + strlen (handle) + 1); p = buf; if (letter || pk) { *p++ = letter; *p++ = ' '; fingerprint_from_pk (pk, array, &n); s = array; for (i=0; i < n ; i++, s++, p += 2) sprintf (p, "%02X", *s); } if (*handle) { *p++ = ' '; for (i=0; handle[i] && i < 100; i++) *p++ = isspace ((unsigned int)handle[i])? '_':handle[i]; } *p = 0; write_status_text ((letter || pk)?STATUS_KEY_CREATED:STATUS_KEY_NOT_CREATED, buf); xfree (buf); } static void print_status_key_not_created (const char *handle) { print_status_key_created (0, NULL, handle); } static void write_uid( KBNODE root, const char *s ) { PACKET *pkt = xmalloc_clear(sizeof *pkt ); size_t n = strlen(s); pkt->pkttype = PKT_USER_ID; pkt->pkt.user_id = xmalloc_clear( sizeof *pkt->pkt.user_id + n - 1 ); pkt->pkt.user_id->len = n; pkt->pkt.user_id->ref = 1; strcpy(pkt->pkt.user_id->name, s); add_kbnode( root, new_kbnode( pkt ) ); } static void do_add_key_flags (PKT_signature *sig, unsigned int use) { byte buf[1]; buf[0] = 0; /* The spec says that all primary keys MUST be able to certify. */ if(sig->sig_class!=0x18) buf[0] |= 0x01; if (use & PUBKEY_USAGE_SIG) buf[0] |= 0x02; if (use & PUBKEY_USAGE_ENC) buf[0] |= 0x04 | 0x08; if (use & PUBKEY_USAGE_AUTH) buf[0] |= 0x20; build_sig_subpkt (sig, SIGSUBPKT_KEY_FLAGS, buf, 1); } int keygen_add_key_expire( PKT_signature *sig, void *opaque ) { PKT_public_key *pk = opaque; byte buf[8]; u32 u; if( pk->expiredate ) { if(pk->expiredate > pk->timestamp) u= pk->expiredate - pk->timestamp; else u= 1; buf[0] = (u >> 24) & 0xff; buf[1] = (u >> 16) & 0xff; buf[2] = (u >> 8) & 0xff; buf[3] = u & 0xff; build_sig_subpkt( sig, SIGSUBPKT_KEY_EXPIRE, buf, 4 ); } else { /* Make sure we don't leave a key expiration subpacket lying around */ delete_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE); } return 0; } static int keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque) { struct opaque_data_usage_and_pk *oduap = opaque; do_add_key_flags (sig, oduap->usage); return keygen_add_key_expire (sig, oduap->pk); } static int set_one_pref (int val, int type, const char *item, byte *buf, int *nbuf) { int i; for (i=0; i < *nbuf; i++ ) if (buf[i] == val) { log_info (_("preference `%s' duplicated\n"), item); return -1; } if (*nbuf >= MAX_PREFS) { if(type==1) log_info(_("too many cipher preferences\n")); else if(type==2) log_info(_("too many digest preferences\n")); else if(type==3) log_info(_("too many compression preferences\n")); else BUG(); return -1; } buf[(*nbuf)++] = val; return 0; } /* * Parse the supplied string and use it to set the standard * preferences. The string may be in a form like the one printed by * "pref" (something like: "S10 S3 H3 H2 Z2 Z1") or the actual * cipher/hash/compress names. Use NULL to set the default * preferences. Returns: 0 = okay */ int keygen_set_std_prefs (const char *string,int personal) { byte sym[MAX_PREFS], hash[MAX_PREFS], zip[MAX_PREFS]; int nsym=0, nhash=0, nzip=0, val, rc=0; int mdc=1, modify=0; /* mdc defaults on, modify defaults off. */ char dummy_string[20*4+1]; /* Enough for 20 items. */ if (!string || !ascii_strcasecmp (string, "default")) { if (opt.def_preference_list) string=opt.def_preference_list; else { dummy_string[0]='\0'; /* The rationale why we use the order AES256,192,128 is for compatibility reasons with PGP. If gpg would define AES128 first, we would get the somewhat confusing situation: gpg -r pgpkey -r gpgkey ---gives--> AES256 gpg -r gpgkey -r pgpkey ---gives--> AES Note that by using --personal-cipher-preferences it is possible to prefer AES128. */ /* Make sure we do not add more than 15 items here, as we could overflow the size of dummy_string. We currently have at most 12. */ if(!check_cipher_algo(CIPHER_ALGO_AES256)) strcat(dummy_string,"S9 "); if(!check_cipher_algo(CIPHER_ALGO_AES192)) strcat(dummy_string,"S8 "); if(!check_cipher_algo(CIPHER_ALGO_AES)) strcat(dummy_string,"S7 "); if(!check_cipher_algo(CIPHER_ALGO_CAST5)) strcat(dummy_string,"S3 "); strcat(dummy_string,"S2 "); /* 3DES */ /* If we have it and we are in PGP2 mode, IDEA goes *after* 3DES so it won't be used unless we're encrypting along with a V3 key. Ideally, we would only put the S1 preference in if the key was RSA and <=2048 bits, as that is what won't break PGP2, but that is difficult with the current code, and not really worth checking as a non-RSA <=2048 bit key wouldn't be usable by PGP2 anyway. -dms */ if(PGP2 && !check_cipher_algo(CIPHER_ALGO_IDEA)) strcat(dummy_string,"S1 "); /* The default hash algo order is: SHA-256, SHA-1, SHA-384, SHA-512, SHA-224. Ordering SHA-1 before SHA-384 might be viewed as a bit strange; it is done because we expect that soon enough SHA-3 will be available and at that point there should be no more need for SHA-384 etc. Anyway this order is just a default and can easily be changed by a config option. */ if (!check_digest_algo (DIGEST_ALGO_SHA256)) strcat (dummy_string, "H8 "); strcat (dummy_string,"H2 ");/* SHA-1 */ if (!check_digest_algo (DIGEST_ALGO_SHA384)) strcat (dummy_string, "H9 "); if (!check_digest_algo (DIGEST_ALGO_SHA512)) strcat (dummy_string, "H10 "); if (!check_digest_algo (DIGEST_ALGO_SHA224)) strcat (dummy_string, "H11 "); /* ZLIB */ strcat(dummy_string,"Z2 "); if(!check_compress_algo(COMPRESS_ALGO_BZIP2)) strcat(dummy_string,"Z3 "); /* ZIP */ strcat(dummy_string,"Z1"); string=dummy_string; } } else if (!ascii_strcasecmp (string, "none")) string = ""; if(strlen(string)) { char *tok,*prefstring; prefstring=xstrdup(string); /* need a writable string! */ while((tok=strsep(&prefstring," ,"))) { if((val=string_to_cipher_algo(tok))) { if(set_one_pref(val,1,tok,sym,&nsym)) rc=-1; } else if((val=string_to_digest_algo(tok))) { if(set_one_pref(val,2,tok,hash,&nhash)) rc=-1; } else if((val=string_to_compress_algo(tok))>-1) { if(set_one_pref(val,3,tok,zip,&nzip)) rc=-1; } else if (ascii_strcasecmp(tok,"mdc")==0) mdc=1; else if (ascii_strcasecmp(tok,"no-mdc")==0) mdc=0; else if (ascii_strcasecmp(tok,"ks-modify")==0) modify=1; else if (ascii_strcasecmp(tok,"no-ks-modify")==0) modify=0; else { log_info (_("invalid item `%s' in preference string\n"),tok); rc=-1; } } xfree(prefstring); } if(!rc) { if(personal) { if(personal==PREFTYPE_SYM) { xfree(opt.personal_cipher_prefs); if(nsym==0) opt.personal_cipher_prefs=NULL; else { int i; opt.personal_cipher_prefs= xmalloc(sizeof(prefitem_t *)*(nsym+1)); for (i=0; iref=1; uid->prefs=xmalloc((sizeof(prefitem_t *)* (nsym_prefs+nhash_prefs+nzip_prefs+1))); for(i=0;iprefs[j].type=PREFTYPE_SYM; uid->prefs[j].value=sym_prefs[i]; } for(i=0;iprefs[j].type=PREFTYPE_HASH; uid->prefs[j].value=hash_prefs[i]; } for(i=0;iprefs[j].type=PREFTYPE_ZIP; uid->prefs[j].value=zip_prefs[i]; } uid->prefs[j].type=PREFTYPE_NONE; uid->prefs[j].value=0; uid->flags.mdc=mdc_available; uid->flags.ks_modify=ks_modify; return uid; } static void add_feature_mdc (PKT_signature *sig,int enabled) { const byte *s; size_t n; int i; char *buf; s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n ); /* Already set or cleared */ if (s && n && ((enabled && (s[0] & 0x01)) || (!enabled && !(s[0] & 0x01)))) return; if (!s || !n) { /* create a new one */ n = 1; buf = xmalloc_clear (n); } else { buf = xmalloc (n); memcpy (buf, s, n); } if(enabled) buf[0] |= 0x01; /* MDC feature */ else buf[0] &= ~0x01; /* Are there any bits set? */ for(i=0;ihashed, SIGSUBPKT_FEATURES); else build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n); xfree (buf); } static void add_keyserver_modify (PKT_signature *sig,int enabled) { const byte *s; size_t n; int i; char *buf; /* The keyserver modify flag is a negative flag (i.e. no-modify) */ enabled=!enabled; s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KS_FLAGS, &n ); /* Already set or cleared */ if (s && n && ((enabled && (s[0] & 0x80)) || (!enabled && !(s[0] & 0x80)))) return; if (!s || !n) { /* create a new one */ n = 1; buf = xmalloc_clear (n); } else { buf = xmalloc (n); memcpy (buf, s, n); } if(enabled) buf[0] |= 0x80; /* no-modify flag */ else buf[0] &= ~0x80; /* Are there any bits set? */ for(i=0;ihashed, SIGSUBPKT_KS_FLAGS); else build_sig_subpkt (sig, SIGSUBPKT_KS_FLAGS, buf, n); xfree (buf); } int keygen_upd_std_prefs( PKT_signature *sig, void *opaque ) { (void)opaque; if (!prefs_initialized) keygen_set_std_prefs (NULL, 0); if (nsym_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_SYM, sym_prefs, nsym_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_SYM); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_SYM); } if (nhash_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, hash_prefs, nhash_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_HASH); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_HASH); } if (nzip_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_COMPR, zip_prefs, nzip_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_COMPR); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_COMPR); } /* Make sure that the MDC feature flag is set if needed */ add_feature_mdc (sig,mdc_available); add_keyserver_modify (sig,ks_modify); keygen_add_keyserver_url(sig,NULL); return 0; } /**************** * Add preference to the self signature packet. * This is only called for packets with version > 3. */ int keygen_add_std_prefs( PKT_signature *sig, void *opaque ) { PKT_public_key *pk = opaque; do_add_key_flags (sig, pk->pubkey_usage); keygen_add_key_expire( sig, opaque ); keygen_upd_std_prefs (sig, opaque); keygen_add_keyserver_url(sig,NULL); return 0; } int keygen_add_keyserver_url(PKT_signature *sig, void *opaque) { const char *url=opaque; if(!url) url=opt.def_keyserver_url; if(url) build_sig_subpkt(sig,SIGSUBPKT_PREF_KS,url,strlen(url)); else delete_sig_subpkt (sig->hashed,SIGSUBPKT_PREF_KS); return 0; } int keygen_add_notations(PKT_signature *sig,void *opaque) { struct notation *notation; /* We always start clean */ delete_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION); delete_sig_subpkt(sig->unhashed,SIGSUBPKT_NOTATION); sig->flags.notation=0; for(notation=opaque;notation;notation=notation->next) if(!notation->flags.ignore) { unsigned char *buf; unsigned int n1,n2; n1=strlen(notation->name); if(notation->altvalue) n2=strlen(notation->altvalue); else if(notation->bdat) n2=notation->blen; else n2=strlen(notation->value); buf = xmalloc( 8 + n1 + n2 ); /* human readable or not */ buf[0] = notation->bdat?0:0x80; buf[1] = buf[2] = buf[3] = 0; buf[4] = n1 >> 8; buf[5] = n1; buf[6] = n2 >> 8; buf[7] = n2; memcpy(buf+8, notation->name, n1 ); if(notation->altvalue) memcpy(buf+8+n1, notation->altvalue, n2 ); else if(notation->bdat) memcpy(buf+8+n1, notation->bdat, n2 ); else memcpy(buf+8+n1, notation->value, n2 ); build_sig_subpkt( sig, SIGSUBPKT_NOTATION | (notation->flags.critical?SIGSUBPKT_FLAG_CRITICAL:0), buf, 8+n1+n2 ); xfree(buf); } return 0; } int keygen_add_revkey(PKT_signature *sig, void *opaque) { struct revocation_key *revkey=opaque; byte buf[2+MAX_FINGERPRINT_LEN]; buf[0]=revkey->class; buf[1]=revkey->algid; memcpy(&buf[2],revkey->fpr,MAX_FINGERPRINT_LEN); build_sig_subpkt(sig,SIGSUBPKT_REV_KEY,buf,2+MAX_FINGERPRINT_LEN); /* All sigs with revocation keys set are nonrevocable */ sig->flags.revocable=0; buf[0] = 0; build_sig_subpkt( sig, SIGSUBPKT_REVOCABLE, buf, 1 ); parse_revkeys(sig); return 0; } /* Create a back-signature. If TIMESTAMP is not NULL, use it for the signature creation time. */ int make_backsig (PKT_signature *sig, PKT_public_key *pk, PKT_public_key *sub_pk, PKT_secret_key *sub_sk, u32 timestamp) { PKT_signature *backsig; int rc; cache_public_key(sub_pk); rc= make_keysig_packet (&backsig, pk, NULL, sub_pk, sub_sk, 0x19, 0, 0, timestamp, 0, NULL, NULL); if(rc) log_error("make_keysig_packet failed for backsig: %s\n",g10_errstr(rc)); else { /* get it into a binary packed form. */ IOBUF backsig_out=iobuf_temp(); PACKET backsig_pkt; init_packet(&backsig_pkt); backsig_pkt.pkttype=PKT_SIGNATURE; backsig_pkt.pkt.signature=backsig; rc=build_packet(backsig_out,&backsig_pkt); free_packet(&backsig_pkt); if(rc) log_error("build_packet failed for backsig: %s\n",g10_errstr(rc)); else { size_t pktlen=0; byte *buf=iobuf_get_temp_buffer(backsig_out); /* Remove the packet header */ if(buf[0]&0x40) { if(buf[1]<192) { pktlen=buf[1]; buf+=2; } else if(buf[1]<224) { pktlen=(buf[1]-192)*256; pktlen+=buf[2]+192; buf+=3; } else if(buf[1]==255) { - pktlen =buf[2] << 24; - pktlen|=buf[3] << 16; - pktlen|=buf[4] << 8; - pktlen|=buf[5]; + pktlen = buf32_to_size_t (buf+2); buf+=6; } else BUG(); } else { int mark=1; switch(buf[0]&3) { case 3: BUG(); break; case 2: - pktlen =buf[mark++] << 24; - pktlen|=buf[mark++] << 16; + pktlen = (size_t)buf[mark++] << 24; + pktlen |= buf[mark++] << 16; case 1: - pktlen|=buf[mark++] << 8; + pktlen |= buf[mark++] << 8; case 0: - pktlen|=buf[mark++]; + pktlen |= buf[mark++]; } buf+=mark; } /* now make the binary blob into a subpacket */ build_sig_subpkt(sig,SIGSUBPKT_SIGNATURE,buf,pktlen); iobuf_close(backsig_out); } } return rc; } static int write_direct_sig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk, struct revocation_key *revkey, u32 timestamp) { PACKET *pkt; PKT_signature *sig; int rc=0; KBNODE node; PKT_public_key *pk; if( opt.verbose ) log_info(_("writing direct signature\n")); /* get the pk packet from the pub_tree */ node = find_kbnode( pub_root, PKT_PUBLIC_KEY ); if( !node ) BUG(); pk = node->pkt->pkt.public_key; /* we have to cache the key, so that the verification of the signature * creation is able to retrieve the public key */ cache_public_key (pk); /* and make the signature */ rc = make_keysig_packet (&sig, pk, NULL, NULL, sk, 0x1F, 0, 0, timestamp, 0, keygen_add_revkey, revkey); if( rc ) { log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) ); return rc; } pkt = xmalloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode( root, new_kbnode( pkt ) ); return rc; } static int write_selfsigs (KBNODE sec_root, KBNODE pub_root, PKT_secret_key *sk, unsigned int use, u32 timestamp) { PACKET *pkt; PKT_signature *sig; PKT_user_id *uid; int rc=0; KBNODE node; PKT_public_key *pk; if( opt.verbose ) log_info(_("writing self signature\n")); /* get the uid packet from the list */ node = find_kbnode( pub_root, PKT_USER_ID ); if( !node ) BUG(); /* no user id packet in tree */ uid = node->pkt->pkt.user_id; /* get the pk packet from the pub_tree */ node = find_kbnode( pub_root, PKT_PUBLIC_KEY ); if( !node ) BUG(); pk = node->pkt->pkt.public_key; pk->pubkey_usage = use; /* we have to cache the key, so that the verification of the signature * creation is able to retrieve the public key */ cache_public_key (pk); /* and make the signature */ rc = make_keysig_packet (&sig, pk, uid, NULL, sk, 0x13, 0, 0, timestamp, 0, keygen_add_std_prefs, pk); if( rc ) { log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) ); return rc; } pkt = xmalloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode( sec_root, new_kbnode( pkt ) ); pkt = xmalloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = copy_signature(NULL,sig); add_kbnode( pub_root, new_kbnode( pkt ) ); return rc; } static int write_keybinding (KBNODE root, KBNODE pub_root, PKT_secret_key *pri_sk, PKT_secret_key *sub_sk, unsigned int use, u32 timestamp) { PACKET *pkt; PKT_signature *sig; int rc=0; KBNODE node; PKT_public_key *pri_pk, *sub_pk; struct opaque_data_usage_and_pk oduap; if( opt.verbose ) log_info(_("writing key binding signature\n")); /* get the pk packet from the pub_tree */ node = find_kbnode( pub_root, PKT_PUBLIC_KEY ); if( !node ) BUG(); pri_pk = node->pkt->pkt.public_key; /* we have to cache the key, so that the verification of the signature * creation is able to retrieve the public key */ cache_public_key (pri_pk); /* find the last subkey */ sub_pk = NULL; for(node=pub_root; node; node = node->next ) { if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) sub_pk = node->pkt->pkt.public_key; } if( !sub_pk ) BUG(); /* and make the signature */ oduap.usage = use; oduap.pk = sub_pk; rc=make_keysig_packet(&sig, pri_pk, NULL, sub_pk, pri_sk, 0x18, 0, 0, timestamp, 0, keygen_add_key_flags_and_expire, &oduap ); if( rc ) { log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) ); return rc; } /* make a backsig */ if(use&PUBKEY_USAGE_SIG) { rc = make_backsig (sig, pri_pk, sub_pk, sub_sk, timestamp); if(rc) return rc; } pkt = xmalloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode( root, new_kbnode( pkt ) ); return rc; } static int gen_elg(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 timestamp, u32 expireval, int is_subkey) { int rc; PACKET *pkt; PKT_secret_key *sk; PKT_public_key *pk; MPI skey[4]; MPI *factors; assert( is_ELGAMAL(algo) ); if (nbits < 1024) { nbits = 2048; log_info(_("keysize invalid; using %u bits\n"), nbits ); } else if (nbits > 4096) { nbits = 4096; log_info(_("keysize invalid; using %u bits\n"), nbits ); } if( (nbits % 32) ) { nbits = ((nbits + 31) / 32) * 32; log_info(_("keysize rounded up to %u bits\n"), nbits ); } rc = pubkey_generate( algo, nbits, skey, &factors ); if( rc ) { log_error("pubkey_generate failed: %s\n", g10_errstr(rc) ); return rc; } sk = xmalloc_clear( sizeof *sk ); pk = xmalloc_clear( sizeof *pk ); sk->timestamp = pk->timestamp = timestamp; sk->version = pk->version = 4; if( expireval ) sk->expiredate = pk->expiredate = sk->timestamp + expireval; sk->pubkey_algo = pk->pubkey_algo = algo; pk->pkey[0] = mpi_copy( skey[0] ); pk->pkey[1] = mpi_copy( skey[1] ); pk->pkey[2] = mpi_copy( skey[2] ); sk->skey[0] = skey[0]; sk->skey[1] = skey[1]; sk->skey[2] = skey[2]; sk->skey[3] = skey[3]; sk->is_protected = 0; sk->protect.algo = 0; sk->csum = checksum_mpi( sk->skey[3] ); if( ret_sk ) /* return an unprotected version of the sk */ *ret_sk = copy_secret_key( NULL, sk ); if( dek ) { sk->protect.algo = dek->algo; sk->protect.s2k = *s2k; rc = protect_secret_key( sk, dek ); if( rc ) { log_error("protect_secret_key failed: %s\n", g10_errstr(rc) ); free_public_key(pk); free_secret_key(sk); return rc; } } pkt = xmalloc_clear(sizeof *pkt); pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; pkt->pkt.public_key = pk; add_kbnode(pub_root, new_kbnode( pkt )); /* don't know whether it makes sense to have the factors, so for now * we store them in the secret keyring (but they are not secret) */ pkt = xmalloc_clear(sizeof *pkt); pkt->pkttype = is_subkey ? PKT_SECRET_SUBKEY : PKT_SECRET_KEY; pkt->pkt.secret_key = sk; add_kbnode(sec_root, new_kbnode( pkt )); return 0; } /**************** * Generate a DSA key */ static int gen_dsa(unsigned int nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 timestamp, u32 expireval, int is_subkey) { int rc; PACKET *pkt; PKT_secret_key *sk; PKT_public_key *pk; MPI skey[5]; MPI *factors; unsigned int qbits; if( nbits < 768) { nbits = 2048; log_info(_("keysize invalid; using %u bits\n"), nbits ); } else if(nbits>3072) { nbits = 3072; log_info(_("keysize invalid; using %u bits\n"), nbits ); } if(nbits % 64) { nbits = ((nbits + 63) / 64) * 64; log_info(_("keysize rounded up to %u bits\n"), nbits ); } /* To comply with FIPS rules we round up to the next value unless in expert mode. */ if (!opt.expert && nbits > 1024 && (nbits % 1024)) { nbits = ((nbits + 1023) / 1024) * 1024; log_info (_("keysize rounded up to %u bits\n"), nbits ); } /* Figure out a q size based on the key size. FIPS 180-3 says: L = 1024, N = 160 L = 2048, N = 224 L = 2048, N = 256 L = 3072, N = 256 2048/256 is an odd pair since there is also a 2048/224 and 3072/256. Matching sizes is not a very exact science. We'll do 256 qbits for nbits over 2047, 224 for nbits over 1024 but less than 2048, and 160 for 1024 (DSA1). */ if(nbits>2047) qbits=256; else if(nbits>1024) qbits=224; else qbits=160; if(qbits!=160) log_info("WARNING: some OpenPGP programs can't" " handle a DSA key with this digest size\n"); rc = dsa2_generate( PUBKEY_ALGO_DSA, nbits, qbits, skey, &factors ); if( rc ) { log_error("dsa2_generate failed: %s\n", g10_errstr(rc) ); return rc; } sk = xmalloc_clear( sizeof *sk ); pk = xmalloc_clear( sizeof *pk ); sk->timestamp = pk->timestamp = timestamp; sk->version = pk->version = 4; if( expireval ) sk->expiredate = pk->expiredate = sk->timestamp + expireval; sk->pubkey_algo = pk->pubkey_algo = PUBKEY_ALGO_DSA; pk->pkey[0] = mpi_copy( skey[0] ); pk->pkey[1] = mpi_copy( skey[1] ); pk->pkey[2] = mpi_copy( skey[2] ); pk->pkey[3] = mpi_copy( skey[3] ); sk->skey[0] = skey[0]; sk->skey[1] = skey[1]; sk->skey[2] = skey[2]; sk->skey[3] = skey[3]; sk->skey[4] = skey[4]; sk->is_protected = 0; sk->protect.algo = 0; sk->csum = checksum_mpi ( sk->skey[4] ); if( ret_sk ) /* return an unprotected version of the sk */ *ret_sk = copy_secret_key( NULL, sk ); if( dek ) { sk->protect.algo = dek->algo; sk->protect.s2k = *s2k; rc = protect_secret_key( sk, dek ); if( rc ) { log_error("protect_secret_key failed: %s\n", g10_errstr(rc) ); free_public_key(pk); free_secret_key(sk); return rc; } } pkt = xmalloc_clear(sizeof *pkt); pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; pkt->pkt.public_key = pk; add_kbnode(pub_root, new_kbnode( pkt )); /* don't know whether it makes sense to have the factors, so for now * we store them in the secret keyring (but they are not secret) * p = 2 * q * f1 * f2 * ... * fn * We store only f1 to f_n-1; fn can be calculated because p and q * are known. */ pkt = xmalloc_clear(sizeof *pkt); pkt->pkttype = is_subkey ? PKT_SECRET_SUBKEY : PKT_SECRET_KEY; pkt->pkt.secret_key = sk; add_kbnode(sec_root, new_kbnode( pkt )); return 0; } /* * Generate an RSA key. */ static int gen_rsa(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 timestamp, u32 expireval, int is_subkey) { int rc; PACKET *pkt; PKT_secret_key *sk; PKT_public_key *pk; MPI skey[6]; MPI *factors; const unsigned maxsize = (opt.flags.large_rsa ? 8192 : 4096); assert( is_RSA(algo) ); if( nbits < 1024 ) { nbits = 2048; log_info(_("keysize invalid; using %u bits\n"), nbits ); } else if (nbits > maxsize) { nbits = maxsize; log_info(_("keysize invalid; using %u bits\n"), nbits ); } if( (nbits % 32) ) { nbits = ((nbits + 31) / 32) * 32; log_info(_("keysize rounded up to %u bits\n"), nbits ); } rc = pubkey_generate( algo, nbits, skey, &factors ); if( rc ) { log_error("pubkey_generate failed: %s\n", g10_errstr(rc) ); return rc; } sk = xmalloc_clear( sizeof *sk ); pk = xmalloc_clear( sizeof *pk ); sk->timestamp = pk->timestamp = timestamp; sk->version = pk->version = 4; if( expireval ) sk->expiredate = pk->expiredate = sk->timestamp + expireval; sk->pubkey_algo = pk->pubkey_algo = algo; pk->pkey[0] = mpi_copy( skey[0] ); pk->pkey[1] = mpi_copy( skey[1] ); sk->skey[0] = skey[0]; sk->skey[1] = skey[1]; sk->skey[2] = skey[2]; sk->skey[3] = skey[3]; sk->skey[4] = skey[4]; sk->skey[5] = skey[5]; sk->is_protected = 0; sk->protect.algo = 0; sk->csum = checksum_mpi (sk->skey[2] ); sk->csum += checksum_mpi (sk->skey[3] ); sk->csum += checksum_mpi (sk->skey[4] ); sk->csum += checksum_mpi (sk->skey[5] ); if( ret_sk ) /* return an unprotected version of the sk */ *ret_sk = copy_secret_key( NULL, sk ); if( dek ) { sk->protect.algo = dek->algo; sk->protect.s2k = *s2k; rc = protect_secret_key( sk, dek ); if( rc ) { log_error("protect_secret_key failed: %s\n", g10_errstr(rc) ); free_public_key(pk); free_secret_key(sk); return rc; } } pkt = xmalloc_clear(sizeof *pkt); pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; pkt->pkt.public_key = pk; add_kbnode(pub_root, new_kbnode( pkt )); pkt = xmalloc_clear(sizeof *pkt); pkt->pkttype = is_subkey ? PKT_SECRET_SUBKEY : PKT_SECRET_KEY; pkt->pkt.secret_key = sk; add_kbnode(sec_root, new_kbnode( pkt )); return 0; } /**************** * check valid days: * return 0 on error or the multiplier */ static int check_valid_days( const char *s ) { if( !digitp(s) ) return 0; for( s++; *s; s++) if( !digitp(s) ) break; if( !*s ) return 1; if( s[1] ) return 0; /* e.g. "2323wc" */ if( *s == 'd' || *s == 'D' ) return 1; if( *s == 'w' || *s == 'W' ) return 7; if( *s == 'm' || *s == 'M' ) return 30; if( *s == 'y' || *s == 'Y' ) return 365; return 0; } static void print_key_flags(int flags) { if(flags&PUBKEY_USAGE_SIG) tty_printf("%s ",_("Sign")); if(flags&PUBKEY_USAGE_CERT) tty_printf("%s ",_("Certify")); if(flags&PUBKEY_USAGE_ENC) tty_printf("%s ",_("Encrypt")); if(flags&PUBKEY_USAGE_AUTH) tty_printf("%s ",_("Authenticate")); } /* Returns the key flags */ static unsigned int ask_key_flags(int algo,int subkey) { /* TRANSLATORS: Please use only plain ASCII characters for the translation. If this is not possible use single digits. Here is a description of the fucntions: s = Toggle signing capability e = Toggle encryption capability a = Toggle authentication capability q = Finish */ const char *togglers=_("SsEeAaQq"); char *answer=NULL; unsigned int current=0; unsigned int possible=openpgp_pk_algo_usage(algo); if ( strlen(togglers) != 8 ) { tty_printf ("NOTE: Bad translation at %s:%d. " "Please report.\n", __FILE__, __LINE__); togglers = "11223300"; } /* Only primary keys may certify. */ if(subkey) possible&=~PUBKEY_USAGE_CERT; /* Preload the current set with the possible set, minus authentication, since nobody really uses auth yet. */ current=possible&~PUBKEY_USAGE_AUTH; for(;;) { tty_printf("\n"); tty_printf(_("Possible actions for a %s key: "), pubkey_algo_to_string(algo)); print_key_flags(possible); tty_printf("\n"); tty_printf(_("Current allowed actions: ")); print_key_flags(current); tty_printf("\n\n"); if(possible&PUBKEY_USAGE_SIG) tty_printf(_(" (%c) Toggle the sign capability\n"), togglers[0]); if(possible&PUBKEY_USAGE_ENC) tty_printf(_(" (%c) Toggle the encrypt capability\n"), togglers[2]); if(possible&PUBKEY_USAGE_AUTH) tty_printf(_(" (%c) Toggle the authenticate capability\n"), togglers[4]); tty_printf(_(" (%c) Finished\n"),togglers[6]); tty_printf("\n"); xfree(answer); answer = cpr_get("keygen.flags",_("Your selection? ")); cpr_kill_prompt(); if(strlen(answer)>1) tty_printf(_("Invalid selection.\n")); else if(*answer=='\0' || *answer==togglers[6] || *answer==togglers[7]) break; else if((*answer==togglers[0] || *answer==togglers[1]) && possible&PUBKEY_USAGE_SIG) { if(current&PUBKEY_USAGE_SIG) current&=~PUBKEY_USAGE_SIG; else current|=PUBKEY_USAGE_SIG; } else if((*answer==togglers[2] || *answer==togglers[3]) && possible&PUBKEY_USAGE_ENC) { if(current&PUBKEY_USAGE_ENC) current&=~PUBKEY_USAGE_ENC; else current|=PUBKEY_USAGE_ENC; } else if((*answer==togglers[4] || *answer==togglers[5]) && possible&PUBKEY_USAGE_AUTH) { if(current&PUBKEY_USAGE_AUTH) current&=~PUBKEY_USAGE_AUTH; else current|=PUBKEY_USAGE_AUTH; } else tty_printf(_("Invalid selection.\n")); } xfree(answer); return current; } /* Ask for an algorithm. The function returns the algorithm id to create. If ADDMODE is false the function won't show an option to create the primary and subkey combined and won't set R_USAGE either. If a combined algorithm has been selected, the subkey algorithm is stored at R_SUBKEY_ALGO. */ static int ask_algo (int addmode, int *r_subkey_algo, unsigned int *r_usage) { char *answer = NULL; int algo; int dummy_algo; if (!r_subkey_algo) r_subkey_algo = &dummy_algo; tty_printf(_("Please select what kind of key you want:\n")); if (!addmode) tty_printf (_(" (%d) RSA and RSA (default)\n"), 1 ); if ( !addmode ) tty_printf (_(" (%d) DSA and Elgamal\n"), 2 ); tty_printf( _(" (%d) DSA (sign only)\n"), 3 ); tty_printf( _(" (%d) RSA (sign only)\n"), 4 ); if (addmode) { tty_printf (_(" (%d) Elgamal (encrypt only)\n"), 5 ); tty_printf (_(" (%d) RSA (encrypt only)\n"), 6 ); } if (opt.expert) { tty_printf (_(" (%d) DSA (set your own capabilities)\n"), 7 ); tty_printf (_(" (%d) RSA (set your own capabilities)\n"), 8 ); } for (;;) { *r_usage = 0; *r_subkey_algo = 0; xfree (answer); answer = cpr_get ("keygen.algo", _("Your selection? ")); cpr_kill_prompt (); algo = *answer? atoi (answer) : 1; if ((algo == 1 || !strcmp (answer, "rsa+rsa")) && !addmode) { algo = PUBKEY_ALGO_RSA; *r_subkey_algo = PUBKEY_ALGO_RSA; break; } else if ((algo == 2 || !strcmp (answer, "dsa+elg")) && !addmode) { algo = PUBKEY_ALGO_DSA; *r_subkey_algo = PUBKEY_ALGO_ELGAMAL_E; break; } else if (algo == 3 || !strcmp (answer, "dsa")) { algo = PUBKEY_ALGO_DSA; *r_usage = PUBKEY_USAGE_SIG; break; } else if (algo == 4 || !strcmp (answer, "rsa/s")) { algo = PUBKEY_ALGO_RSA; *r_usage = PUBKEY_USAGE_SIG; break; } else if ((algo == 5 || !strcmp (answer, "elg")) && addmode) { algo = PUBKEY_ALGO_ELGAMAL_E; *r_usage = PUBKEY_USAGE_ENC; break; } else if ((algo == 6 || !strcmp (answer, "rsa/e")) && addmode) { algo = PUBKEY_ALGO_RSA; *r_usage = PUBKEY_USAGE_ENC; break; } else if ((algo == 7 || !strcmp (answer, "dsa/*")) && opt.expert) { algo = PUBKEY_ALGO_DSA; *r_usage = ask_key_flags (algo, addmode); break; } else if ((algo == 8 || !strcmp (answer, "rsa/*")) && opt.expert) { algo = PUBKEY_ALGO_RSA; *r_usage = ask_key_flags (algo, addmode); break; } else tty_printf (_("Invalid selection.\n")); } xfree(answer); return algo; } /* Ask for the key size. ALGO is the algorithm. If PRIMARY_KEYSIZE is not 0, the function asks for the size of the encryption subkey. */ static unsigned int ask_keysize (int algo, unsigned int primary_keysize) { unsigned nbits, min, def=2048, max=4096; int for_subkey = !!primary_keysize; int autocomp = 0; if(opt.expert) min=512; else min=1024; if (primary_keysize && !opt.expert) { /* Deduce the subkey size from the primary key size. */ if (algo == PUBKEY_ALGO_DSA && primary_keysize > 3072) nbits = 3072; /* For performance reasons we don't support more than 3072 bit DSA. However we won't see this case anyway because DSA can't be used as an encryption subkey ;-). */ else nbits = primary_keysize; autocomp = 1; goto leave; } switch(algo) { case PUBKEY_ALGO_DSA: def=2048; max=3072; break; case PUBKEY_ALGO_RSA: min=1024; break; } tty_printf(_("%s keys may be between %u and %u bits long.\n"), pubkey_algo_to_string(algo),min,max); for(;;) { char *prompt,*answer; if (for_subkey) prompt = xasprintf (_("What keysize do you want " "for the subkey? (%u) "), def); else prompt = xasprintf (_("What keysize do you want? (%u) "), def); answer = cpr_get("keygen.size",prompt); cpr_kill_prompt(); nbits = *answer? atoi(answer): def; xfree(prompt); xfree(answer); if(nbitsmax) tty_printf(_("%s keysizes must be in the range %u-%u\n"), pubkey_algo_to_string(algo),min,max); else break; } tty_printf(_("Requested keysize is %u bits\n"), nbits ); leave: if( algo == PUBKEY_ALGO_DSA && (nbits % 64) ) { nbits = ((nbits + 63) / 64) * 64; if (!autocomp) tty_printf (_("rounded up to %u bits\n"), nbits); } else if( (nbits % 32) ) { nbits = ((nbits + 31) / 32) * 32; if (!autocomp) tty_printf (_("rounded up to %u bits\n"), nbits); } return nbits; } /**************** * Parse an expire string and return its value in seconds. * Returns (u32)-1 on error. * This isn't perfect since scan_isodatestr returns unix time, and * OpenPGP actually allows a 32-bit time *plus* a 32-bit offset. * Because of this, we only permit setting expirations up to 2106, but * OpenPGP could theoretically allow up to 2242. I think we'll all * just cope for the next few years until we get a 64-bit time_t or * similar. */ u32 parse_expire_string (u32 timestamp, const char *string) { int mult; u32 seconds; u32 abs_date = 0; if ( !*string ) seconds = 0; else if ( !strncmp (string, "seconds=", 8) ) seconds = atoi (string+8); else if( (abs_date = scan_isodatestr(string)) && abs_date > timestamp ) seconds = abs_date - timestamp; else if( (mult=check_valid_days(string)) ) seconds = atoi(string) * 86400L * mult; else seconds=(u32)-1; return seconds; } /* Parse an Creation-Date string which is either "1986-04-26" or "19860426T042640". Returns 0 on error. */ static u32 parse_creation_string (const char *string) { u32 seconds; if (!*string) seconds = 0; else if ( !strncmp (string, "seconds=", 8) ) seconds = atoi (string+8); else if ( !(seconds = scan_isodatestr (string))) seconds = isotime2seconds (string); return seconds; } /* object == 0 for a key, and 1 for a sig */ u32 ask_expire_interval(u32 timestamp,int object,const char *def_expire) { u32 interval; char *answer; switch(object) { case 0: if(def_expire) BUG(); tty_printf(_("Please specify how long the key should be valid.\n" " 0 = key does not expire\n" " = key expires in n days\n" " w = key expires in n weeks\n" " m = key expires in n months\n" " y = key expires in n years\n")); break; case 1: if(!def_expire) BUG(); tty_printf(_("Please specify how long the signature should be valid.\n" " 0 = signature does not expire\n" " = signature expires in n days\n" " w = signature expires in n weeks\n" " m = signature expires in n months\n" " y = signature expires in n years\n")); break; default: BUG(); } /* Note: The elgamal subkey for DSA has no expiration date because * it must be signed with the DSA key and this one has the expiration * date */ answer = NULL; for(;;) { xfree(answer); if(object==0) answer = cpr_get("keygen.valid",_("Key is valid for? (0) ")); else { char *prompt; #define PROMPTSTRING _("Signature is valid for? (%s) ") /* This will actually end up larger than necessary because of the 2 bytes for '%s' */ prompt=xmalloc(strlen(PROMPTSTRING)+strlen(def_expire)+1); sprintf(prompt,PROMPTSTRING,def_expire); #undef PROMPTSTRING answer = cpr_get("siggen.valid",prompt); xfree(prompt); if(*answer=='\0') answer=xstrdup(def_expire); } cpr_kill_prompt(); trim_spaces(answer); interval = parse_expire_string (timestamp, answer); if( interval == (u32)-1 ) { tty_printf(_("invalid value\n")); continue; } if( !interval ) { tty_printf((object==0) ? _("Key does not expire at all\n") : _("Signature does not expire at all\n")); } else { tty_printf(object==0 ? _("Key expires at %s\n") : _("Signature expires at %s\n"), asctimestamp((ulong)(timestamp + interval) ) ); #if SIZEOF_TIME_T <= 4 && !defined(HAVE_UNSIGNED_TIME_T) if ((time_t)((ulong)(timestamp+interval)) < 0 ) tty_printf (_("Your system can't display dates beyond 2038.\n" "However, it will be correctly handled up to" " 2106.\n")); else #endif /*SIZEOF_TIME_T*/ if ( (time_t)((unsigned long)(timestamp+interval)) < timestamp ) { tty_printf (_("invalid value\n")); continue; } } if( cpr_enabled() || cpr_get_answer_is_yes("keygen.valid.okay", _("Is this correct? (y/N) ")) ) break; } xfree(answer); return interval; } static char * ask_user_id( int mode ) { char *answer; char *aname, *acomment, *amail, *uid; if( !mode ) tty_printf( _("\n" "You need a user ID to identify your key; " "the software constructs the user ID\n" "from the Real Name, Comment and Email Address in this form:\n" " \"Heinrich Heine (Der Dichter) \"\n\n") ); uid = aname = acomment = amail = NULL; for(;;) { char *p; int fail=0; if( !aname ) { for(;;) { xfree(aname); aname = cpr_get("keygen.name",_("Real name: ")); trim_spaces(aname); cpr_kill_prompt(); if( opt.allow_freeform_uid ) break; if( strpbrk( aname, "<>" ) ) tty_printf(_("Invalid character in name\n")); else if( digitp(aname) ) tty_printf(_("Name may not start with a digit\n")); else if( strlen(aname) < 5 ) tty_printf(_("Name must be at least 5 characters long\n")); else break; } } if( !amail ) { for(;;) { xfree(amail); amail = cpr_get("keygen.email",_("Email address: ")); trim_spaces(amail); cpr_kill_prompt(); if( !*amail || opt.allow_freeform_uid ) break; /* no email address is okay */ else if ( !is_valid_mailbox (amail) ) tty_printf(_("Not a valid email address\n")); else break; } } if( !acomment ) { for(;;) { xfree(acomment); acomment = cpr_get("keygen.comment",_("Comment: ")); trim_spaces(acomment); cpr_kill_prompt(); if( !*acomment ) break; /* no comment is okay */ else if( strpbrk( acomment, "()" ) ) tty_printf(_("Invalid character in comment\n")); else break; } } xfree(uid); uid = p = xmalloc(strlen(aname)+strlen(amail)+strlen(acomment)+12+10); p = stpcpy(p, aname ); if( *acomment ) p = stpcpy(stpcpy(stpcpy(p," ("), acomment),")"); if( *amail ) p = stpcpy(stpcpy(stpcpy(p," <"), amail),">"); /* append a warning if we do not have dev/random * or it is switched into quick testmode */ if( quick_random_gen(-1) ) strcpy(p, " (INSECURE!)" ); /* print a note in case that UTF8 mapping has to be done */ for(p=uid; *p; p++ ) { if( *p & 0x80 ) { tty_printf(_("You are using the `%s' character set.\n"), get_native_charset() ); break; } } tty_printf(_("You selected this USER-ID:\n \"%s\"\n\n"), uid); /* fixme: add a warning if this user-id already exists */ if( !*amail && !opt.allow_freeform_uid && (strchr( aname, '@' ) || strchr( acomment, '@'))) { fail = 1; tty_printf(_("Please don't put the email address " "into the real name or the comment\n") ); } for(;;) { /* TRANSLATORS: These are the allowed answers in lower and uppercase. Below you will find the matching string which should be translated accordingly and the letter changed to match the one in the answer string. n = Change name c = Change comment e = Change email o = Okay (ready, continue) q = Quit */ const char *ansstr = _("NnCcEeOoQq"); if( strlen(ansstr) != 10 ) BUG(); if( cpr_enabled() ) { answer = xstrdup (ansstr + (fail?8:6)); answer[1] = 0; } else { answer = cpr_get("keygen.userid.cmd", fail? _("Change (N)ame, (C)omment, (E)mail or (Q)uit? ") : _("Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? ")); cpr_kill_prompt(); } if( strlen(answer) > 1 ) ; else if( *answer == ansstr[0] || *answer == ansstr[1] ) { xfree(aname); aname = NULL; break; } else if( *answer == ansstr[2] || *answer == ansstr[3] ) { xfree(acomment); acomment = NULL; break; } else if( *answer == ansstr[4] || *answer == ansstr[5] ) { xfree(amail); amail = NULL; break; } else if( *answer == ansstr[6] || *answer == ansstr[7] ) { if( fail ) { tty_printf(_("Please correct the error first\n")); } else { xfree(aname); aname = NULL; xfree(acomment); acomment = NULL; xfree(amail); amail = NULL; break; } } else if( *answer == ansstr[8] || *answer == ansstr[9] ) { xfree(aname); aname = NULL; xfree(acomment); acomment = NULL; xfree(amail); amail = NULL; xfree(uid); uid = NULL; break; } xfree(answer); } xfree(answer); if( !aname && !acomment && !amail ) break; xfree(uid); uid = NULL; } if( uid ) { char *p = native_to_utf8( uid ); xfree( uid ); uid = p; } return uid; } /* FIXME: We need a way to cancel this prompt. */ static DEK * do_ask_passphrase( STRING2KEY **ret_s2k ) { DEK *dek = NULL; STRING2KEY *s2k; const char *errtext = NULL; tty_printf(_("You need a Passphrase to protect your secret key.\n\n") ); s2k = xmalloc_secure( sizeof *s2k ); for(;;) { s2k->mode = opt.s2k_mode; s2k->hash_algo = S2K_DIGEST_ALGO; dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k,2, errtext, NULL); if( !dek ) { errtext = N_("passphrase not correctly repeated; try again"); tty_printf(_("%s.\n"), _(errtext)); } else if( !dek->keylen ) { xfree(dek); dek = NULL; xfree(s2k); s2k = NULL; tty_printf(_( "You don't want a passphrase - this is probably a *bad* idea!\n" "I will do it anyway. You can change your passphrase at any time,\n" "using this program with the option \"--edit-key\".\n\n")); break; } else break; /* okay */ } *ret_s2k = s2k; return dek; } static int do_create( int algo, unsigned int nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek, STRING2KEY *s2k, PKT_secret_key **sk, u32 timestamp, u32 expiredate, int is_subkey ) { int rc=0; if( !opt.batch ) tty_printf(_( "We need to generate a lot of random bytes. It is a good idea to perform\n" "some other action (type on the keyboard, move the mouse, utilize the\n" "disks) during the prime generation; this gives the random number\n" "generator a better chance to gain enough entropy.\n") ); if( algo == PUBKEY_ALGO_ELGAMAL_E ) rc = gen_elg(algo, nbits, pub_root, sec_root, dek, s2k, sk, timestamp, expiredate, is_subkey); else if( algo == PUBKEY_ALGO_DSA ) rc = gen_dsa(nbits, pub_root, sec_root, dek, s2k, sk, timestamp, expiredate, is_subkey); else if( algo == PUBKEY_ALGO_RSA ) rc = gen_rsa(algo, nbits, pub_root, sec_root, dek, s2k, sk, timestamp, expiredate, is_subkey); else BUG(); return rc; } /**************** * Generate a new user id packet, or return NULL if canceled */ PKT_user_id * generate_user_id() { PKT_user_id *uid; char *p; size_t n; p = ask_user_id( 1 ); if( !p ) return NULL; n = strlen(p); uid = xmalloc_clear( sizeof *uid + n ); uid->len = n; strcpy(uid->name, p); uid->ref = 1; return uid; } static void release_parameter_list( struct para_data_s *r ) { struct para_data_s *r2; for( ; r ; r = r2 ) { r2 = r->next; if( r->key == pPASSPHRASE_DEK ) xfree( r->u.dek ); else if( r->key == pPASSPHRASE_S2K ) xfree( r->u.s2k ); xfree(r); } } static struct para_data_s * get_parameter( struct para_data_s *para, enum para_name key ) { struct para_data_s *r; for( r = para; r && r->key != key; r = r->next ) ; return r; } static const char * get_parameter_value( struct para_data_s *para, enum para_name key ) { struct para_data_s *r = get_parameter( para, key ); return (r && *r->u.value)? r->u.value : NULL; } static int get_parameter_algo( struct para_data_s *para, enum para_name key ) { int i; struct para_data_s *r = get_parameter( para, key ); if( !r ) return -1; if( digitp( r->u.value ) ) i = atoi( r->u.value ); else i = string_to_pubkey_algo( r->u.value ); if (i == PUBKEY_ALGO_RSA_E || i == PUBKEY_ALGO_RSA_S) i = 0; /* we don't want to allow generation of these algorithms */ return i; } /* * parse the usage parameter and set the keyflags. Return true on error. */ static int parse_parameter_usage (const char *fname, struct para_data_s *para, enum para_name key) { struct para_data_s *r = get_parameter( para, key ); char *p, *pn; unsigned int use; if( !r ) return 0; /* none (this is an optional parameter)*/ use = 0; pn = r->u.value; while ( (p = strsep (&pn, " \t,")) ) { if ( !*p) ; else if ( !ascii_strcasecmp (p, "sign") ) use |= PUBKEY_USAGE_SIG; else if ( !ascii_strcasecmp (p, "encrypt") ) use |= PUBKEY_USAGE_ENC; else if ( !ascii_strcasecmp (p, "auth") ) use |= PUBKEY_USAGE_AUTH; else { log_error("%s:%d: invalid usage list\n", fname, r->lnr ); return -1; /* error */ } } r->u.usage = use; return 1; } static int parse_revocation_key (const char *fname, struct para_data_s *para, enum para_name key) { struct para_data_s *r = get_parameter( para, key ); struct revocation_key revkey; char *pn; int i; if( !r ) return 0; /* none (this is an optional parameter) */ pn = r->u.value; revkey.class=0x80; revkey.algid=atoi(pn); if(!revkey.algid) goto fail; /* Skip to the fpr */ while(*pn && *pn!=':') pn++; if(*pn!=':') goto fail; pn++; for(i=0;iu.revkey,&revkey,sizeof(struct revocation_key)); return 0; fail: log_error("%s:%d: invalid revocation key\n", fname, r->lnr ); return -1; /* error */ } static u32 get_parameter_u32( struct para_data_s *para, enum para_name key ) { struct para_data_s *r; r = get_parameter (para, key); if (!r && key == pKEYCREATIONDATE) { /* Return a default for the creation date if it has not yet been set. We need to set this into the parameter list so that a second call for pKEYCREATIONDATE returns the same value. The default is the current time unless an explicit creation date has been specified. Checking the creation date here is only for the case that it has not yet been parsed. */ r = get_parameter (para, pCREATIONDATE); if (r && *r->u.value) { u32 seconds; seconds = parse_creation_string (r->u.value); if (!seconds) log_error ("invalid creation date in line %d\n", r->lnr ); else /* Okay: Change this parameter. */ { r->u.creation = seconds; r->key = pKEYCREATIONDATE; } } r = get_parameter (para, key); if (!r) { /* Create a new parameter. */ r = xmalloc_clear (sizeof *r); r->key = key; r->u.creation = make_timestamp (); r->next = para; para = r; } r = get_parameter (para, key); assert (r); } if (!r) return 0; if( r->key == pKEYCREATIONDATE ) return r->u.creation; if( r->key == pKEYEXPIRE || r->key == pSUBKEYEXPIRE ) return r->u.expire; if( r->key == pKEYUSAGE || r->key == pSUBKEYUSAGE ) return r->u.usage; return (unsigned int)strtoul( r->u.value, NULL, 10 ); } static unsigned int get_parameter_uint( struct para_data_s *para, enum para_name key ) { return get_parameter_u32( para, key ); } static DEK * get_parameter_dek( struct para_data_s *para, enum para_name key ) { struct para_data_s *r = get_parameter( para, key ); return r? r->u.dek : NULL; } static STRING2KEY * get_parameter_s2k( struct para_data_s *para, enum para_name key ) { struct para_data_s *r = get_parameter( para, key ); return r? r->u.s2k : NULL; } static struct revocation_key * get_parameter_revkey( struct para_data_s *para, enum para_name key ) { struct para_data_s *r = get_parameter( para, key ); return r? &r->u.revkey : NULL; } static int proc_parameter_file( struct para_data_s *para, const char *fname, struct output_control_s *outctrl, int card ) { struct para_data_s *r; const char *s1, *s2, *s3; size_t n; char *p; int have_user_id=0,err,algo; /* Check that we have all required parameters. */ r = get_parameter( para, pKEYTYPE ); if(r) { algo=get_parameter_algo(para,pKEYTYPE); if(check_pubkey_algo2(algo,PUBKEY_USAGE_SIG)) { log_error("%s:%d: invalid algorithm\n", fname, r->lnr ); return -1; } } else { log_error("%s: no Key-Type specified\n",fname); return -1; } err = parse_parameter_usage (fname, para, pKEYUSAGE); if (!err) { /* Default to algo capabilities if key-usage is not provided */ r = xmalloc_clear(sizeof(*r)); r->key = pKEYUSAGE; r->u.usage = openpgp_pk_algo_usage(algo); r->next = para; para = r; } else if (err == -1) return -1; else { r = get_parameter (para, pKEYUSAGE); if (r && (r->u.usage & ~openpgp_pk_algo_usage (algo))) { log_error ("%s:%d: specified Key-Usage not allowed for algo %d\n", fname, r->lnr, algo); return -1; } } r = get_parameter( para, pSUBKEYTYPE ); if(r) { algo = get_parameter_algo (para, pSUBKEYTYPE); if (check_pubkey_algo (algo)) { log_error ("%s:%d: invalid algorithm\n", fname, r->lnr ); return -1; } err = parse_parameter_usage (fname, para, pSUBKEYUSAGE); if (!err) { /* Default to algo capabilities if subkey-usage is not provided */ r = xmalloc_clear (sizeof(*r)); r->key = pSUBKEYUSAGE; r->u.usage = openpgp_pk_algo_usage (algo); r->next = para; para = r; } else if (err == -1) return -1; else { r = get_parameter (para, pSUBKEYUSAGE); if (r && (r->u.usage & ~openpgp_pk_algo_usage (algo))) { log_error ("%s:%d: specified Subkey-Usage not allowed" " for algo %d\n", fname, r->lnr, algo); return -1; } } } if( get_parameter_value( para, pUSERID ) ) have_user_id=1; else { /* create the formatted user ID */ s1 = get_parameter_value( para, pNAMEREAL ); s2 = get_parameter_value( para, pNAMECOMMENT ); s3 = get_parameter_value( para, pNAMEEMAIL ); if( s1 || s2 || s3 ) { n = (s1?strlen(s1):0) + (s2?strlen(s2):0) + (s3?strlen(s3):0); r = xmalloc_clear( sizeof *r + n + 20 ); r->key = pUSERID; p = r->u.value; if( s1 ) p = stpcpy(p, s1 ); if( s2 ) p = stpcpy(stpcpy(stpcpy(p," ("), s2 ),")"); if( s3 ) p = stpcpy(stpcpy(stpcpy(p," <"), s3 ),">"); r->next = para; para = r; have_user_id=1; } } if(!have_user_id) { log_error("%s: no User-ID specified\n",fname); return -1; } /* Set preferences, if any. */ keygen_set_std_prefs(get_parameter_value( para, pPREFERENCES ), 0); /* Set keyserver, if any. */ s1=get_parameter_value( para, pKEYSERVER ); if(s1) { struct keyserver_spec *spec; spec=parse_keyserver_uri(s1,1,NULL,0); if(spec) { free_keyserver_spec(spec); opt.def_keyserver_url=s1; } else { log_error("%s:%d: invalid keyserver url\n", fname, r->lnr ); return -1; } } /* Set revoker, if any. */ if (parse_revocation_key (fname, para, pREVOKER)) return -1; /* make DEK and S2K from the Passphrase */ r = get_parameter( para, pPASSPHRASE ); if( r && *r->u.value ) { /* We have a plain text passphrase - create a DEK from it. * It is a little bit ridiculous to keep it in secure memory * but because we do this alwasy, why not here. */ STRING2KEY *s2k; DEK *dek; s2k = xmalloc_secure( sizeof *s2k ); s2k->mode = opt.s2k_mode; s2k->hash_algo = S2K_DIGEST_ALGO; set_next_passphrase( r->u.value ); dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2, NULL, NULL); set_next_passphrase( NULL ); assert( dek ); memset( r->u.value, 0, strlen(r->u.value) ); r = xmalloc_clear( sizeof *r ); r->key = pPASSPHRASE_S2K; r->u.s2k = s2k; r->next = para; para = r; r = xmalloc_clear( sizeof *r ); r->key = pPASSPHRASE_DEK; r->u.dek = dek; r->next = para; para = r; } /* Make KEYCREATIONDATE from Creation-Date. */ r = get_parameter (para, pCREATIONDATE); if (r && *r->u.value) { u32 seconds; seconds = parse_creation_string (r->u.value); if (!seconds) { log_error ("%s:%d: invalid creation date\n", fname, r->lnr ); return -1; } r->u.creation = seconds; r->key = pKEYCREATIONDATE; /* Change that entry. */ } /* Make KEYEXPIRE from Expire-Date. */ r = get_parameter( para, pEXPIREDATE ); if( r && *r->u.value ) { u32 seconds; seconds = parse_expire_string (get_parameter_u32 (para, pKEYCREATIONDATE), r->u.value); if (seconds == (u32)(-1)) { log_error("%s:%d: invalid expire date\n", fname, r->lnr ); return -1; } r->u.expire = seconds; r->key = pKEYEXPIRE; /* change hat entry */ /* also set it for the subkey */ r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYEXPIRE; r->u.expire = seconds; r->next = para; para = r; } if( !!outctrl->pub.newfname ^ !!outctrl->sec.newfname ) { log_error("%s:%d: only one ring name is set\n", fname, outctrl->lnr ); return -1; } do_generate_keypair (para, outctrl, card); return 0; } /**************** * Kludge to allow non interactive key generation controlled * by a parameter file. * Note, that string parameters are expected to be in UTF-8 */ static void read_parameter_file( const char *fname ) { static struct { const char *name; enum para_name key; } keywords[] = { { "Key-Type", pKEYTYPE}, { "Key-Length", pKEYLENGTH }, { "Key-Usage", pKEYUSAGE }, { "Subkey-Type", pSUBKEYTYPE }, { "Subkey-Length", pSUBKEYLENGTH }, { "Subkey-Usage", pSUBKEYUSAGE }, { "Name-Real", pNAMEREAL }, { "Name-Email", pNAMEEMAIL }, { "Name-Comment", pNAMECOMMENT }, { "Expire-Date", pEXPIREDATE }, { "Creation-Date", pCREATIONDATE }, { "Passphrase", pPASSPHRASE }, { "Preferences", pPREFERENCES }, { "Revoker", pREVOKER }, { "Handle", pHANDLE }, { "Keyserver", pKEYSERVER }, { NULL, 0 } }; IOBUF fp; byte *line; unsigned int maxlen, nline; char *p; int lnr; const char *err = NULL; struct para_data_s *para, *r; int i; struct output_control_s outctrl; memset( &outctrl, 0, sizeof( outctrl ) ); if( !fname || !*fname) fname = "-"; fp = iobuf_open (fname); if (fp && is_secured_file (iobuf_get_fd (fp))) { iobuf_close (fp); fp = NULL; errno = EPERM; } if (!fp) { log_error (_("can't open `%s': %s\n"), fname, strerror(errno) ); return; } iobuf_ioctl (fp, 3, 1, NULL); /* No file caching. */ lnr = 0; err = NULL; para = NULL; maxlen = 1024; line = NULL; while ( iobuf_read_line (fp, &line, &nline, &maxlen) ) { char *keyword, *value; lnr++; if( !maxlen ) { err = "line too long"; break; } for( p = line; isspace(*(byte*)p); p++ ) ; if( !*p || *p == '#' ) continue; keyword = p; if( *keyword == '%' ) { for( ; !isspace(*(byte*)p); p++ ) ; if( *p ) *p++ = 0; for( ; isspace(*(byte*)p); p++ ) ; value = p; trim_trailing_ws( value, strlen(value) ); if( !ascii_strcasecmp( keyword, "%echo" ) ) log_info("%s\n", value ); else if( !ascii_strcasecmp( keyword, "%dry-run" ) ) outctrl.dryrun = 1; else if( !ascii_strcasecmp( keyword, "%commit" ) ) { outctrl.lnr = lnr; if (proc_parameter_file( para, fname, &outctrl, 0 )) print_status_key_not_created (get_parameter_value (para, pHANDLE)); release_parameter_list( para ); para = NULL; } else if( !ascii_strcasecmp( keyword, "%pubring" ) ) { if( outctrl.pub.fname && !strcmp( outctrl.pub.fname, value ) ) ; /* still the same file - ignore it */ else { xfree( outctrl.pub.newfname ); outctrl.pub.newfname = xstrdup( value ); outctrl.use_files = 1; } } else if( !ascii_strcasecmp( keyword, "%secring" ) ) { if( outctrl.sec.fname && !strcmp( outctrl.sec.fname, value ) ) ; /* still the same file - ignore it */ else { xfree( outctrl.sec.newfname ); outctrl.sec.newfname = xstrdup( value ); outctrl.use_files = 1; } } else log_info("skipping control `%s' (%s)\n", keyword, value ); continue; } if( !(p = strchr( p, ':' )) || p == keyword ) { err = "missing colon"; break; } if( *p ) *p++ = 0; for( ; isspace(*(byte*)p); p++ ) ; if( !*p ) { err = "missing argument"; break; } value = p; trim_trailing_ws( value, strlen(value) ); for(i=0; keywords[i].name; i++ ) { if( !ascii_strcasecmp( keywords[i].name, keyword ) ) break; } if( !keywords[i].name ) { err = "unknown keyword"; break; } if( keywords[i].key != pKEYTYPE && !para ) { err = "parameter block does not start with \"Key-Type\""; break; } if( keywords[i].key == pKEYTYPE && para ) { outctrl.lnr = lnr; if (proc_parameter_file( para, fname, &outctrl, 0 )) print_status_key_not_created (get_parameter_value (para, pHANDLE)); release_parameter_list( para ); para = NULL; } else { for( r = para; r; r = r->next ) { if( r->key == keywords[i].key ) break; } if( r ) { err = "duplicate keyword"; break; } } r = xmalloc_clear( sizeof *r + strlen( value ) ); r->lnr = lnr; r->key = keywords[i].key; strcpy( r->u.value, value ); r->next = para; para = r; } if( err ) log_error("%s:%d: %s\n", fname, lnr, err ); else if( iobuf_error (fp) ) { log_error("%s:%d: read error\n", fname, lnr); } else if( para ) { outctrl.lnr = lnr; if (proc_parameter_file( para, fname, &outctrl, 0 )) print_status_key_not_created (get_parameter_value (para, pHANDLE)); } if( outctrl.use_files ) { /* close open streams */ iobuf_close( outctrl.pub.stream ); iobuf_close( outctrl.sec.stream ); /* Must invalidate that ugly cache to actually close it. */ if (outctrl.pub.fname) iobuf_ioctl (NULL, 2, 0, (char*)outctrl.pub.fname); if (outctrl.sec.fname) iobuf_ioctl (NULL, 2, 0, (char*)outctrl.sec.fname); xfree( outctrl.pub.fname ); xfree( outctrl.pub.newfname ); xfree( outctrl.sec.fname ); xfree( outctrl.sec.newfname ); } release_parameter_list( para ); iobuf_close (fp); } /* * Generate a keypair (fname is only used in batch mode) If * CARD_SERIALNO is not NULL the fucntion will create the keys on an * OpenPGP Card. If BACKUP_ENCRYPTION_DIR has been set and * CARD_SERIALNO is NOT NULL, the encryption key for the card gets * generate in software, imported to the card and a backup file * written to directory given by this argument . */ void generate_keypair (const char *fname, const char *card_serialno, const char *backup_encryption_dir) { unsigned int nbits; char *uid = NULL; DEK *dek; STRING2KEY *s2k; int algo; unsigned int use; int both = 0; u32 expire; struct para_data_s *para = NULL; struct para_data_s *r; struct output_control_s outctrl; memset( &outctrl, 0, sizeof( outctrl ) ); if (opt.batch && card_serialno) { /* We don't yet support unattended key generation. */ log_error (_("can't do this in batch mode\n")); return; } if (opt.batch) { read_parameter_file( fname ); return; } if (card_serialno) { #ifdef ENABLE_CARD_SUPPORT r = xcalloc (1, sizeof *r + strlen (card_serialno) ); r->key = pSERIALNO; strcpy( r->u.value, card_serialno); r->next = para; para = r; algo = PUBKEY_ALGO_RSA; r = xcalloc (1, sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pKEYUSAGE; strcpy (r->u.value, "sign"); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pSUBKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pSUBKEYUSAGE; strcpy (r->u.value, "encrypt"); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pAUTHKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; if (backup_encryption_dir) { r = xcalloc (1, sizeof *r + strlen (backup_encryption_dir) ); r->key = pBACKUPENCDIR; strcpy (r->u.value, backup_encryption_dir); r->next = para; para = r; } #endif /*ENABLE_CARD_SUPPORT*/ } else { int subkey_algo; algo = ask_algo (0, &subkey_algo, &use ); if (subkey_algo) { /* Create primary and subkey at once. */ both = 1; r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf (r->u.value, "%d", algo); r->next = para; para = r; nbits = ask_keysize (algo, 0); r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYLENGTH; sprintf( r->u.value, "%u", nbits); r->next = para; para = r; r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYUSAGE; strcpy( r->u.value, "sign" ); r->next = para; para = r; r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYTYPE; sprintf( r->u.value, "%d", subkey_algo ); r->next = para; para = r; r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYUSAGE; strcpy( r->u.value, "encrypt" ); r->next = para; para = r; } else { r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; if (use) { r = xmalloc_clear( sizeof *r + 25 ); r->key = pKEYUSAGE; sprintf( r->u.value, "%s%s%s", (use & PUBKEY_USAGE_SIG)? "sign ":"", (use & PUBKEY_USAGE_ENC)? "encrypt ":"", (use & PUBKEY_USAGE_AUTH)? "auth":"" ); r->next = para; para = r; } nbits = 0; } nbits = ask_keysize (both? subkey_algo : algo, nbits); r = xmalloc_clear( sizeof *r + 20 ); r->key = both? pSUBKEYLENGTH : pKEYLENGTH; sprintf( r->u.value, "%u", nbits); r->next = para; para = r; } expire = ask_expire_interval (get_parameter_u32 (para, pKEYCREATIONDATE), 0, NULL); r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYEXPIRE; r->u.expire = expire; r->next = para; para = r; r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYEXPIRE; r->u.expire = expire; r->next = para; para = r; uid = ask_user_id(0); if( !uid ) { log_error(_("Key generation canceled.\n")); release_parameter_list( para ); return; } r = xmalloc_clear( sizeof *r + strlen(uid) ); r->key = pUSERID; strcpy( r->u.value, uid ); r->next = para; para = r; dek = card_serialno? NULL : do_ask_passphrase( &s2k ); if( dek ) { r = xmalloc_clear( sizeof *r ); r->key = pPASSPHRASE_DEK; r->u.dek = dek; r->next = para; para = r; r = xmalloc_clear( sizeof *r ); r->key = pPASSPHRASE_S2K; r->u.s2k = s2k; r->next = para; para = r; } proc_parameter_file( para, "[internal]", &outctrl, !!card_serialno); release_parameter_list( para ); } #ifdef ENABLE_CARD_SUPPORT /* Generate a raw key and return it as a secret key packet. The function will ask for the passphrase and return a protected as well as an unprotected copy of a new secret key packet. 0 is returned on success and the caller must then free the returned values. */ static int generate_raw_key (int algo, unsigned int nbits, u32 created_at, PKT_secret_key **r_sk_unprotected, PKT_secret_key **r_sk_protected) { int rc; DEK *dek = NULL; STRING2KEY *s2k = NULL; PKT_secret_key *sk = NULL; int i; size_t nskey, npkey; npkey = pubkey_get_npkey (algo); nskey = pubkey_get_nskey (algo); assert (nskey <= PUBKEY_MAX_NSKEY && npkey < nskey); if (nbits < 512) { nbits = 512; log_info (_("keysize invalid; using %u bits\n"), nbits ); } if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; log_info(_("keysize rounded up to %u bits\n"), nbits ); } dek = do_ask_passphrase (&s2k); sk = xmalloc_clear (sizeof *sk); sk->timestamp = created_at; sk->version = 4; sk->pubkey_algo = algo; rc = pubkey_generate (algo, nbits, sk->skey, NULL); if (rc) { log_error("pubkey_generate failed: %s\n", g10_errstr(rc) ); goto leave; } for (i=npkey; i < nskey; i++) sk->csum += checksum_mpi (sk->skey[i]); if (r_sk_unprotected) *r_sk_unprotected = copy_secret_key (NULL, sk); if (dek) { sk->protect.algo = dek->algo; sk->protect.s2k = *s2k; rc = protect_secret_key (sk, dek); if (rc) { log_error ("protect_secret_key failed: %s\n", g10_errstr(rc)); goto leave; } } if (r_sk_protected) { *r_sk_protected = sk; sk = NULL; } leave: if (sk) free_secret_key (sk); xfree (dek); xfree (s2k); return rc; } #endif /* ENABLE_CARD_SUPPORT */ /* Create and delete a dummy packet to start off a list of kbnodes. */ static void start_tree(KBNODE *tree) { PACKET *pkt; pkt=xmalloc_clear(sizeof(*pkt)); pkt->pkttype=PKT_NONE; *tree=new_kbnode(pkt); delete_kbnode(*tree); } static void do_generate_keypair (struct para_data_s *para,struct output_control_s *outctrl, int card) { KBNODE pub_root = NULL; KBNODE sec_root = NULL; PKT_secret_key *pri_sk = NULL, *sub_sk = NULL; const char *s; struct revocation_key *revkey; int rc; int did_sub = 0; u32 timestamp; if( outctrl->dryrun ) { log_info("dry-run mode - key generation skipped\n"); return; } if( outctrl->use_files ) { if( outctrl->pub.newfname ) { iobuf_close(outctrl->pub.stream); outctrl->pub.stream = NULL; if (outctrl->pub.fname) iobuf_ioctl (NULL, 2, 0, (char*)outctrl->pub.fname); xfree( outctrl->pub.fname ); outctrl->pub.fname = outctrl->pub.newfname; outctrl->pub.newfname = NULL; if (is_secured_filename (outctrl->pub.fname) ) { outctrl->pub.stream = NULL; errno = EPERM; } else outctrl->pub.stream = iobuf_create( outctrl->pub.fname ); if( !outctrl->pub.stream ) { log_error(_("can't create `%s': %s\n"), outctrl->pub.newfname, strerror(errno) ); return; } if( opt.armor ) { outctrl->pub.afx.what = 1; iobuf_push_filter( outctrl->pub.stream, armor_filter, &outctrl->pub.afx ); } } if( outctrl->sec.newfname ) { mode_t oldmask; iobuf_close(outctrl->sec.stream); outctrl->sec.stream = NULL; if (outctrl->sec.fname) iobuf_ioctl (NULL, 2, 0, (char*)outctrl->sec.fname); xfree( outctrl->sec.fname ); outctrl->sec.fname = outctrl->sec.newfname; outctrl->sec.newfname = NULL; oldmask = umask (077); if (is_secured_filename (outctrl->sec.fname) ) { outctrl->sec.stream = NULL; errno = EPERM; } else outctrl->sec.stream = iobuf_create( outctrl->sec.fname ); umask (oldmask); if( !outctrl->sec.stream ) { log_error(_("can't create `%s': %s\n"), outctrl->sec.newfname, strerror(errno) ); return; } if( opt.armor ) { outctrl->sec.afx.what = 5; iobuf_push_filter( outctrl->sec.stream, armor_filter, &outctrl->sec.afx ); } } assert( outctrl->pub.stream ); assert( outctrl->sec.stream ); if( opt.verbose ) { log_info(_("writing public key to `%s'\n"), outctrl->pub.fname ); if (card) log_info (_("writing secret key stub to `%s'\n"), outctrl->sec.fname); else log_info(_("writing secret key to `%s'\n"), outctrl->sec.fname ); } } /* We create the packets as a tree of kbnodes. Because the * structure we create is known in advance we simply generate a * linked list. The first packet is a dummy packet which we flag * as deleted. The very first packet must always be a KEY packet. */ start_tree(&pub_root); start_tree(&sec_root); timestamp = get_parameter_u32 (para, pKEYCREATIONDATE); /* Note that, depending on the backend (i.e. the used scdaemon version or the internal code), the card key generation may update TIMESTAMP for each key. Thus we need to pass TIMESTAMP to all signing function to make sure that the binding signature is done using the timestamp of the corresponding (sub)key and not that of the primary key. An alternative implementation could tell the signing function the node of the subkey but that is more work than just to pass the current timestamp. */ if (!card) { rc = do_create( get_parameter_algo( para, pKEYTYPE ), get_parameter_uint( para, pKEYLENGTH ), pub_root, sec_root, get_parameter_dek( para, pPASSPHRASE_DEK ), get_parameter_s2k( para, pPASSPHRASE_S2K ), &pri_sk, timestamp, get_parameter_u32( para, pKEYEXPIRE ), 0 ); } else { rc = gen_card_key (PUBKEY_ALGO_RSA, 1, 1, pub_root, sec_root, NULL, ×tamp, get_parameter_u32 (para, pKEYEXPIRE), para); if (!rc) { pri_sk = sec_root->next->pkt->pkt.secret_key; assert (pri_sk); } } if(!rc && (revkey=get_parameter_revkey(para,pREVOKER))) { rc = write_direct_sig (pub_root, pub_root, pri_sk, revkey, timestamp); if (!rc) write_direct_sig (sec_root, pub_root, pri_sk, revkey, timestamp); } if( !rc && (s=get_parameter_value(para, pUSERID)) ) { write_uid(pub_root, s ); if( !rc ) write_uid(sec_root, s ); if (!rc) rc = write_selfsigs (sec_root, pub_root, pri_sk, get_parameter_uint (para, pKEYUSAGE), timestamp); } /* Write the auth key to the card before the encryption key. This is a partial workaround for a PGP bug (as of this writing, all versions including 8.1), that causes it to try and encrypt to the most recent subkey regardless of whether that subkey is actually an encryption type. In this case, the auth key is an RSA key so it succeeds. */ if (!rc && card && get_parameter (para, pAUTHKEYTYPE)) { rc = gen_card_key (PUBKEY_ALGO_RSA, 3, 0, pub_root, sec_root, NULL, ×tamp, get_parameter_u32 (para, pKEYEXPIRE), para); if (!rc) rc = write_keybinding (pub_root, pub_root, pri_sk, sub_sk, PUBKEY_USAGE_AUTH, timestamp); if (!rc) rc = write_keybinding (sec_root, pub_root, pri_sk, sub_sk, PUBKEY_USAGE_AUTH, timestamp); } if( !rc && get_parameter( para, pSUBKEYTYPE ) ) { if (!card) { rc = do_create( get_parameter_algo( para, pSUBKEYTYPE ), get_parameter_uint( para, pSUBKEYLENGTH ), pub_root, sec_root, get_parameter_dek( para, pPASSPHRASE_DEK ), get_parameter_s2k( para, pPASSPHRASE_S2K ), &sub_sk, timestamp, get_parameter_u32( para, pSUBKEYEXPIRE ), 1 ); } else { if ((s = get_parameter_value (para, pBACKUPENCDIR))) { /* A backup of the encryption key has been requested. Generate the key i software and import it then to the card. Write a backup file. */ rc = gen_card_key_with_backup (PUBKEY_ALGO_RSA, 2, 0, pub_root, sec_root, timestamp, get_parameter_u32 (para, pKEYEXPIRE), para, s); } else rc = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root, sec_root, NULL, ×tamp, get_parameter_u32 (para, pKEYEXPIRE), para); } if( !rc ) rc = write_keybinding (pub_root, pub_root, pri_sk, sub_sk, get_parameter_uint (para, pSUBKEYUSAGE), timestamp ); if( !rc ) rc = write_keybinding (sec_root, pub_root, pri_sk, sub_sk, get_parameter_uint (para, pSUBKEYUSAGE), timestamp); did_sub = 1; } if( !rc && outctrl->use_files ) { /* direct write to specified files */ rc = write_keyblock( outctrl->pub.stream, pub_root ); if( rc ) log_error("can't write public key: %s\n", g10_errstr(rc) ); if( !rc ) { rc = write_keyblock( outctrl->sec.stream, sec_root ); if( rc ) log_error("can't write secret key: %s\n", g10_errstr(rc) ); } } else if( !rc ) { /* write to the standard keyrings */ KEYDB_HANDLE pub_hd = keydb_new (0); KEYDB_HANDLE sec_hd = keydb_new (1); /* FIXME: we may have to create the keyring first */ rc = keydb_locate_writable (pub_hd, NULL); if (rc) log_error (_("no writable public keyring found: %s\n"), g10_errstr (rc)); if (!rc) { rc = keydb_locate_writable (sec_hd, NULL); if (rc) log_error (_("no writable secret keyring found: %s\n"), g10_errstr (rc)); } if (!rc && opt.verbose) { log_info(_("writing public key to `%s'\n"), keydb_get_resource_name (pub_hd)); if (card) log_info (_("writing secret key stub to `%s'\n"), keydb_get_resource_name (sec_hd)); else log_info(_("writing secret key to `%s'\n"), keydb_get_resource_name (sec_hd)); } if (!rc) { rc = keydb_insert_keyblock (pub_hd, pub_root); if (rc) log_error (_("error writing public keyring `%s': %s\n"), keydb_get_resource_name (pub_hd), g10_errstr(rc)); } if (!rc) { rc = keydb_insert_keyblock (sec_hd, sec_root); if (rc) log_error (_("error writing secret keyring `%s': %s\n"), keydb_get_resource_name (pub_hd), g10_errstr(rc)); } keydb_release (pub_hd); keydb_release (sec_hd); if (!rc) { int no_enc_rsa = get_parameter_algo(para, pKEYTYPE) == PUBKEY_ALGO_RSA && get_parameter_uint( para, pKEYUSAGE ) && !(get_parameter_uint( para,pKEYUSAGE) & PUBKEY_USAGE_ENC); PKT_public_key *pk = find_kbnode (pub_root, PKT_PUBLIC_KEY)->pkt->pkt.public_key; keyid_from_pk(pk,pk->main_keyid); register_trusted_keyid(pk->main_keyid); update_ownertrust (pk, ((get_ownertrust (pk) & ~TRUST_MASK) | TRUST_ULTIMATE )); if (!opt.batch) { tty_printf(_("public and secret key created and signed.\n") ); tty_printf("\n"); list_keyblock(pub_root,0,1,NULL); } if( !opt.batch && ( get_parameter_algo( para, pKEYTYPE ) == PUBKEY_ALGO_DSA || no_enc_rsa ) && !get_parameter( para, pSUBKEYTYPE ) ) { tty_printf(_("Note that this key cannot be used for " "encryption. You may want to use\n" "the command \"--edit-key\" to generate a " "subkey for this purpose.\n") ); } } } if( rc ) { if( opt.batch ) log_error("key generation failed: %s\n", g10_errstr(rc) ); else tty_printf(_("Key generation failed: %s\n"), g10_errstr(rc) ); print_status_key_not_created ( get_parameter_value (para, pHANDLE) ); } else { PKT_public_key *pk = find_kbnode (pub_root, PKT_PUBLIC_KEY)->pkt->pkt.public_key; print_status_key_created (did_sub? 'B':'P', pk, get_parameter_value (para, pHANDLE)); } release_kbnode( pub_root ); release_kbnode( sec_root ); if( pri_sk && !card) /* the unprotected secret key unless we have a */ free_secret_key(pri_sk); /* shallow copy in card mode. */ if( sub_sk ) free_secret_key(sub_sk); } /**************** * add a new subkey to an existing key. * Returns true if a new key has been generated and put into the keyblocks. */ int generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ) { int okay=0, rc=0; KBNODE node; PKT_secret_key *pri_sk = NULL, *sub_sk = NULL; int algo; unsigned int use; u32 expire; unsigned nbits; char *passphrase = NULL; DEK *dek = NULL; STRING2KEY *s2k = NULL; u32 timestamp; int ask_pass = 0; /* break out the primary secret key */ node = find_kbnode( sec_keyblock, PKT_SECRET_KEY ); if( !node ) { log_error("Oops; secret key not found anymore!\n"); goto leave; } /* make a copy of the sk to keep the protected one in the keyblock */ pri_sk = copy_secret_key( NULL, node->pkt->pkt.secret_key ); timestamp = make_timestamp(); if( pri_sk->timestamp > timestamp ) { ulong d = pri_sk->timestamp - timestamp; log_info( d==1 ? _("key has been created %lu second " "in future (time warp or clock problem)\n") : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); if( !opt.ignore_time_conflict ) { rc = G10ERR_TIME_CONFLICT; goto leave; } } if (pri_sk->version < 4) { log_info (_("NOTE: creating subkeys for v3 keys " "is not OpenPGP compliant\n")); goto leave; } if (pri_sk->is_protected && pri_sk->protect.s2k.mode == 1001) { tty_printf(_("Secret parts of primary key are not available.\n")); rc = G10ERR_NO_SECKEY; goto leave; } /* Unprotect to get the passphrase. */ switch( is_secret_key_protected( pri_sk ) ) { case -1: rc = G10ERR_PUBKEY_ALGO; break; case 0: tty_printf(_("This key is not protected.\n")); break; case -2: tty_printf(_("Secret parts of primary key are stored on-card.\n")); ask_pass = 1; break; default: tty_printf(_("Key is protected.\n")); rc = check_secret_key( pri_sk, 0 ); if( !rc ) passphrase = get_last_passphrase(); break; } if( rc ) goto leave; algo = ask_algo (1, NULL, &use); assert(algo); nbits = ask_keysize (algo, 0); expire = ask_expire_interval(timestamp,0,NULL); if( !cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay", _("Really create? (y/N) "))) goto leave; if (ask_pass) dek = do_ask_passphrase (&s2k); else if (passphrase) { s2k = xmalloc_secure( sizeof *s2k ); s2k->mode = opt.s2k_mode; s2k->hash_algo = S2K_DIGEST_ALGO; set_next_passphrase( passphrase ); dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2, NULL, NULL ); } rc = do_create (algo, nbits, pub_keyblock, sec_keyblock, dek, s2k, &sub_sk, timestamp, expire, 1 ); xfree( dek ); if (!rc) rc = write_keybinding (pub_keyblock, pub_keyblock, pri_sk, sub_sk, use, timestamp); if (!rc) rc = write_keybinding (sec_keyblock, pub_keyblock, pri_sk, sub_sk, use, timestamp); if (!rc) { okay = 1; write_status_text (STATUS_KEY_CREATED, "S"); } leave: if( rc ) log_error(_("Key generation failed: %s\n"), g10_errstr(rc) ); xfree( passphrase ); xfree( s2k ); /* release the copy of the (now unprotected) secret keys */ if( pri_sk ) free_secret_key(pri_sk); if( sub_sk ) free_secret_key(sub_sk); set_next_passphrase( NULL ); return okay; } #ifdef ENABLE_CARD_SUPPORT /* Generate a subkey on a card. */ int generate_card_subkeypair (KBNODE pub_keyblock, KBNODE sec_keyblock, int keyno, const char *serialno) { int okay=0, rc=0; KBNODE node; PKT_secret_key *pri_sk = NULL, *sub_sk; int algo; unsigned int use; u32 timestamp,expire; char *passphrase = NULL; struct para_data_s *para = NULL; assert (keyno >= 1 && keyno <= 3); para = xcalloc (1, sizeof *para + strlen (serialno) ); para->key = pSERIALNO; strcpy (para->u.value, serialno); /* Break out the primary secret key */ node = find_kbnode( sec_keyblock, PKT_SECRET_KEY ); if(!node) { log_error("Oops; secret key not found anymore!\n"); goto leave; } /* Make a copy of the sk to keep the protected one in the keyblock */ pri_sk = copy_secret_key (NULL, node->pkt->pkt.secret_key); timestamp = make_timestamp(); if (pri_sk->timestamp > timestamp) { ulong d = pri_sk->timestamp - timestamp; log_info (d==1 ? _("key has been created %lu second " "in future (time warp or clock problem)\n") : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); if (!opt.ignore_time_conflict) { rc = G10ERR_TIME_CONFLICT; goto leave; } } if (pri_sk->version < 4) { log_info (_("NOTE: creating subkeys for v3 keys " "is not OpenPGP compliant\n")); goto leave; } /* Unprotect to get the passphrase. */ switch( is_secret_key_protected (pri_sk) ) { case -1: rc = G10ERR_PUBKEY_ALGO; break; case 0: tty_printf("This key is not protected.\n"); break; default: tty_printf("Key is protected.\n"); rc = check_secret_key( pri_sk, 0 ); if (!rc) passphrase = get_last_passphrase(); break; } if (rc) goto leave; algo = PUBKEY_ALGO_RSA; expire = ask_expire_interval (timestamp,0,NULL); if (keyno == 1) use = PUBKEY_USAGE_SIG; else if (keyno == 2) use = PUBKEY_USAGE_ENC; else use = PUBKEY_USAGE_AUTH; if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.cardsub.okay", _("Really create? (y/N) "))) goto leave; if (passphrase) set_next_passphrase (passphrase); rc = gen_card_key (algo, keyno, 0, pub_keyblock, sec_keyblock, &sub_sk, ×tamp, expire, para); if (!rc) rc = write_keybinding (pub_keyblock, pub_keyblock, pri_sk, sub_sk, use, timestamp); if (!rc) rc = write_keybinding (sec_keyblock, pub_keyblock, pri_sk, sub_sk, use, timestamp); if (!rc) { okay = 1; write_status_text (STATUS_KEY_CREATED, "S"); } leave: if (rc) log_error (_("Key generation failed: %s\n"), g10_errstr(rc) ); xfree (passphrase); /* Release the copy of the (now unprotected) secret keys. */ if (pri_sk) free_secret_key (pri_sk); set_next_passphrase( NULL ); release_parameter_list (para); return okay; } #endif /* !ENABLE_CARD_SUPPORT */ /**************** * Write a keyblock to an output stream */ static int write_keyblock( IOBUF out, KBNODE node ) { for( ; node ; node = node->next ) { if(!is_deleted_kbnode(node)) { int rc = build_packet( out, node->pkt ); if( rc ) { log_error("build_packet(%d) failed: %s\n", node->pkt->pkttype, g10_errstr(rc) ); return G10ERR_WRITE_FILE; } } } return 0; } /* Note that TIMESTAMP is an in/out arg. */ static int gen_card_key (int algo, int keyno, int is_primary, KBNODE pub_root, KBNODE sec_root, PKT_secret_key **ret_sk, u32 *timestamp, u32 expireval, struct para_data_s *para) { #ifdef ENABLE_CARD_SUPPORT int rc; const char *s; struct agent_card_genkey_s info; PACKET *pkt; PKT_secret_key *sk; PKT_public_key *pk; assert (algo == PUBKEY_ALGO_RSA); /* Fixme: We don't have the serialnumber available, thus passing NULL. */ rc = agent_scd_genkey (&info, keyno, 1, NULL, timestamp); /* if (gpg_err_code (rc) == GPG_ERR_EEXIST) */ /* { */ /* tty_printf ("\n"); */ /* log_error ("WARNING: key does already exists!\n"); */ /* tty_printf ("\n"); */ /* if ( cpr_get_answer_is_yes( "keygen.card.replace_key", */ /* _("Replace existing key? "))) */ /* rc = agent_scd_genkey (&info, keyno, 1); */ /* } */ if (rc) { log_error ("key generation failed: %s\n", gpg_strerror (rc)); return rc; } if ( !info.n || !info.e ) { log_error ("communication error with SCD\n"); mpi_free (info.n); mpi_free (info.e); return gpg_error (GPG_ERR_GENERAL); } if (*timestamp != info.created_at) log_info ("Note that the key does not use the suggested creation date\n"); *timestamp = info.created_at; pk = xcalloc (1, sizeof *pk ); sk = xcalloc (1, sizeof *sk ); sk->timestamp = pk->timestamp = info.created_at; sk->version = pk->version = 4; if (expireval) sk->expiredate = pk->expiredate = pk->timestamp + expireval; sk->pubkey_algo = pk->pubkey_algo = algo; pk->pkey[0] = info.n; pk->pkey[1] = info.e; sk->skey[0] = mpi_copy (pk->pkey[0]); sk->skey[1] = mpi_copy (pk->pkey[1]); sk->skey[2] = mpi_set_opaque (NULL, xstrdup ("dummydata"), 10); sk->is_protected = 1; sk->protect.s2k.mode = 1002; s = get_parameter_value (para, pSERIALNO); if (s) { for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1]; sk->protect.ivlen++, s += 2) sk->protect.iv[sk->protect.ivlen] = xtoi_2 (s); } if( ret_sk ) *ret_sk = sk; pkt = xcalloc (1,sizeof *pkt); pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; pkt->pkt.public_key = pk; add_kbnode(pub_root, new_kbnode( pkt )); pkt = xcalloc (1,sizeof *pkt); pkt->pkttype = is_primary ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; pkt->pkt.secret_key = sk; add_kbnode(sec_root, new_kbnode( pkt )); return 0; #else return -1; #endif /*!ENABLE_CARD_SUPPORT*/ } static int gen_card_key_with_backup (int algo, int keyno, int is_primary, KBNODE pub_root, KBNODE sec_root, u32 timestamp, u32 expireval, struct para_data_s *para, const char *backup_dir) { #ifdef ENABLE_CARD_SUPPORT int rc; const char *s; PACKET *pkt; PKT_secret_key *sk, *sk_unprotected, *sk_protected; PKT_public_key *pk; size_t n; int i; unsigned int nbits; sk_unprotected = NULL; sk_protected = NULL; /* Get the size of the key directly from the card. */ { struct agent_card_info_s info; memset (&info, 0, sizeof info); if (!agent_scd_getattr ("KEY-ATTR", &info) && info.key_attr[1].algo) nbits = info.key_attr[1].nbits; else nbits = 1024; /* All pre-v2.0 cards. */ agent_release_card_info (&info); } rc = generate_raw_key (algo, nbits, timestamp, &sk_unprotected, &sk_protected); if (rc) return rc; /* First, store the key to the card. */ rc = save_unprotected_key_to_card (sk_unprotected, keyno); if (rc) { log_error (_("storing key onto card failed: %s\n"), g10_errstr (rc)); free_secret_key (sk_unprotected); free_secret_key (sk_protected); return rc; } /* Get rid of the secret key parameters and store the serial numer. */ sk = sk_unprotected; n = pubkey_get_nskey (sk->pubkey_algo); for (i=pubkey_get_npkey (sk->pubkey_algo); i < n; i++) { mpi_free (sk->skey[i]); sk->skey[i] = NULL; } i = pubkey_get_npkey (sk->pubkey_algo); sk->skey[i] = mpi_set_opaque (NULL, xstrdup ("dummydata"), 10); sk->is_protected = 1; sk->protect.s2k.mode = 1002; s = get_parameter_value (para, pSERIALNO); assert (s); for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1]; sk->protect.ivlen++, s += 2) sk->protect.iv[sk->protect.ivlen] = xtoi_2 (s); /* Now write the *protected* secret key to the file. */ { char name_buffer[50]; char *fname; IOBUF fp; mode_t oldmask; keyid_from_sk (sk, NULL); sprintf (name_buffer,"sk_%08lX%08lX.gpg", (ulong)sk->keyid[0], (ulong)sk->keyid[1]); fname = make_filename (backup_dir, name_buffer, NULL); oldmask = umask (077); if (is_secured_filename (fname)) { fp = NULL; errno = EPERM; } else fp = iobuf_create (fname); umask (oldmask); if (!fp) { log_error (_("can't create backup file `%s': %s\n"), fname, strerror(errno) ); xfree (fname); free_secret_key (sk_unprotected); free_secret_key (sk_protected); return G10ERR_OPEN_FILE; } pkt = xcalloc (1, sizeof *pkt); pkt->pkttype = PKT_SECRET_KEY; pkt->pkt.secret_key = sk_protected; sk_protected = NULL; rc = build_packet (fp, pkt); if (rc) { log_error("build packet failed: %s\n", g10_errstr(rc) ); iobuf_cancel (fp); } else { byte array[MAX_FINGERPRINT_LEN]; char *fprbuf, *p; iobuf_close (fp); iobuf_ioctl (NULL, 2, 0, (char*)fname); log_info (_("NOTE: backup of card key saved to `%s'\n"), fname); fingerprint_from_sk (sk, array, &n); p = fprbuf = xmalloc (MAX_FINGERPRINT_LEN*2 + 1 + 1); for (i=0; i < n ; i++, p += 2) sprintf (p, "%02X", array[i]); *p++ = ' '; *p = 0; write_status_text_and_buffer (STATUS_BACKUP_KEY_CREATED, fprbuf, fname, strlen (fname), 0); xfree (fprbuf); } free_packet (pkt); xfree (pkt); xfree (fname); if (rc) { free_secret_key (sk_unprotected); return rc; } } /* Create the public key from the secret key. */ pk = xcalloc (1, sizeof *pk ); pk->timestamp = sk->timestamp; pk->version = sk->version; if (expireval) pk->expiredate = sk->expiredate = sk->timestamp + expireval; pk->pubkey_algo = sk->pubkey_algo; n = pubkey_get_npkey (sk->pubkey_algo); for (i=0; i < n; i++) pk->pkey[i] = mpi_copy (sk->skey[i]); /* Build packets and add them to the node lists. */ pkt = xcalloc (1,sizeof *pkt); pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; pkt->pkt.public_key = pk; add_kbnode(pub_root, new_kbnode( pkt )); pkt = xcalloc (1,sizeof *pkt); pkt->pkttype = is_primary ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; pkt->pkt.secret_key = sk; add_kbnode(sec_root, new_kbnode( pkt )); return 0; #else return -1; #endif /*!ENABLE_CARD_SUPPORT*/ } #ifdef ENABLE_CARD_SUPPORT int save_unprotected_key_to_card (PKT_secret_key *sk, int keyno) { int rc; unsigned char *rsa_n = NULL; unsigned char *rsa_e = NULL; unsigned char *rsa_p = NULL; unsigned char *rsa_q = NULL; unsigned int rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len; unsigned char *sexp = NULL; unsigned char *p; char numbuf[55], numbuf2[50]; assert (is_RSA (sk->pubkey_algo)); assert (!sk->is_protected); /* Copy the parameters into straight buffers. */ rsa_n = mpi_get_secure_buffer (sk->skey[0], &rsa_n_len, NULL); rsa_e = mpi_get_secure_buffer (sk->skey[1], &rsa_e_len, NULL); rsa_p = mpi_get_secure_buffer (sk->skey[3], &rsa_p_len, NULL); rsa_q = mpi_get_secure_buffer (sk->skey[4], &rsa_q_len, NULL); if (!rsa_n || !rsa_e || !rsa_p || !rsa_q) { rc = G10ERR_INV_ARG; goto leave; } /* Put the key into an S-expression. */ sexp = p = xmalloc_secure (30 + rsa_n_len + rsa_e_len + rsa_p_len + rsa_q_len + 4*sizeof (numbuf) + 25 + sizeof(numbuf) + 20); p = stpcpy (p,"(11:private-key(3:rsa(1:n"); sprintf (numbuf, "%u:", rsa_n_len); p = stpcpy (p, numbuf); memcpy (p, rsa_n, rsa_n_len); p += rsa_n_len; sprintf (numbuf, ")(1:e%u:", rsa_e_len); p = stpcpy (p, numbuf); memcpy (p, rsa_e, rsa_e_len); p += rsa_e_len; sprintf (numbuf, ")(1:p%u:", rsa_p_len); p = stpcpy (p, numbuf); memcpy (p, rsa_p, rsa_p_len); p += rsa_p_len; sprintf (numbuf, ")(1:q%u:", rsa_q_len); p = stpcpy (p, numbuf); memcpy (p, rsa_q, rsa_q_len); p += rsa_q_len; p = stpcpy (p,"))(10:created-at"); sprintf (numbuf2, "%lu", (unsigned long)sk->timestamp); sprintf (numbuf, "%lu:", (unsigned long)strlen (numbuf2)); p = stpcpy (stpcpy (stpcpy (p, numbuf), numbuf2), "))"); /* Fixme: Unfortunately we don't have the serialnumber available - thus we can't pass it down to the agent. */ rc = agent_scd_writekey (keyno, NULL, sexp, p - sexp); leave: xfree (sexp); xfree (rsa_n); xfree (rsa_e); xfree (rsa_p); xfree (rsa_q); return rc; } #endif /*ENABLE_CARD_SUPPORT*/ diff --git a/g10/keyid.c b/g10/keyid.c index d7072d42f..ed30cff31 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -1,762 +1,756 @@ /* keyid.c - key ID and fingerprint handling * Copyright (C) 1998, 1999, 2000, 2001, 2003, * 2004 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 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include "util.h" #include "main.h" #include "packet.h" #include "options.h" #include "mpi.h" #include "keydb.h" #include "i18n.h" +#include "host2net.h" #ifdef HAVE_UNSIGNED_TIME_T # define INVALID_TIME_CHECK(a) ((a) == (time_t)(-1)) #else /* Error or 32 bit time_t and value after 2038-01-19. */ # define INVALID_TIME_CHECK(a) ((a) < 0) #endif int pubkey_letter( int algo ) { switch( algo ) { case PUBKEY_ALGO_RSA: return 'R' ; case PUBKEY_ALGO_RSA_E: return 'r' ; case PUBKEY_ALGO_RSA_S: return 's' ; case PUBKEY_ALGO_ELGAMAL_E: return 'g'; case PUBKEY_ALGO_ELGAMAL: return 'G' ; case PUBKEY_ALGO_DSA: return 'D' ; case PUBKEY_ALGO_ECDSA: return 'E' ; /* ECC DSA (sign only) */ case PUBKEY_ALGO_ECDH: return 'e' ; /* ECC DH (encrypt only) */ case PUBKEY_ALGO_ECC: return 'C' ; /* ECC generic */ default: return '?'; } } /* This function is useful for v4 fingerprints and v3 or v4 key signing. */ void hash_public_key( MD_HANDLE md, PKT_public_key *pk ) { unsigned n=6; unsigned nb[PUBKEY_MAX_NPKEY]; unsigned nn[PUBKEY_MAX_NPKEY]; byte *pp[PUBKEY_MAX_NPKEY]; int i; int npkey = pubkey_get_npkey( pk->pubkey_algo ); /* Two extra bytes for the expiration date in v3 */ if(pk->version<4) n+=2; if(npkey==0 && pk->pkey[0] && mpi_is_opaque(pk->pkey[0])) { pp[0]=mpi_get_opaque(pk->pkey[0],&nn[0]); n+=nn[0]; } else for(i=0; i < npkey; i++ ) { nb[i] = mpi_get_nbits(pk->pkey[i]); pp[i] = mpi_get_buffer( pk->pkey[i], nn+i, NULL ); n += 2 + nn[i]; } md_putc( md, 0x99 ); /* ctb */ /* What does it mean if n is greater than than 0xFFFF ? */ md_putc( md, n >> 8 ); /* 2 byte length header */ md_putc( md, n ); md_putc( md, pk->version ); md_putc( md, pk->timestamp >> 24 ); md_putc( md, pk->timestamp >> 16 ); md_putc( md, pk->timestamp >> 8 ); md_putc( md, pk->timestamp ); if(pk->version<4) { u16 days=0; if(pk->expiredate) days=(u16)((pk->expiredate - pk->timestamp) / 86400L); md_putc( md, days >> 8 ); md_putc( md, days ); } md_putc( md, pk->pubkey_algo ); if(npkey==0 && pk->pkey[0] && mpi_is_opaque(pk->pkey[0])) md_write(md,pp[0],nn[0]); else for(i=0; i < npkey; i++ ) { md_putc( md, nb[i]>>8); md_putc( md, nb[i] ); md_write( md, pp[i], nn[i] ); xfree(pp[i]); } } static MD_HANDLE do_fingerprint_md( PKT_public_key *pk ) { MD_HANDLE md; md = md_open( DIGEST_ALGO_SHA1, 0); hash_public_key(md,pk); md_final( md ); return md; } static MD_HANDLE do_fingerprint_md_sk( PKT_secret_key *sk ) { PKT_public_key pk; int npkey = pubkey_get_npkey( sk->pubkey_algo ); /* npkey is correct! */ int i; if(npkey==0) return NULL; pk.pubkey_algo = sk->pubkey_algo; pk.version = sk->version; pk.timestamp = sk->timestamp; pk.expiredate = sk->expiredate; pk.pubkey_algo = sk->pubkey_algo; for( i=0; i < npkey; i++ ) pk.pkey[i] = sk->skey[i]; return do_fingerprint_md( &pk ); } size_t keystrlen(void) { switch(opt.keyid_format) { case KF_SHORT: return 8; case KF_LONG: return 16; case KF_0xSHORT: return 10; case KF_0xLONG: return 18; default: BUG(); } } const char * keystr(u32 *keyid) { static char keyid_str[19]; switch(opt.keyid_format) { case KF_SHORT: sprintf(keyid_str,"%08lX",(ulong)keyid[1]); break; case KF_LONG: if(keyid[0]) sprintf(keyid_str,"%08lX%08lX",(ulong)keyid[0],(ulong)keyid[1]); else sprintf(keyid_str,"%08lX",(ulong)keyid[1]); break; case KF_0xSHORT: sprintf(keyid_str,"0x%08lX",(ulong)keyid[1]); break; case KF_0xLONG: if(keyid[0]) sprintf(keyid_str,"0x%08lX%08lX",(ulong)keyid[0],(ulong)keyid[1]); else sprintf(keyid_str,"0x%08lX",(ulong)keyid[1]); break; default: BUG(); } return keyid_str; } const char * keystr_from_pk(PKT_public_key *pk) { keyid_from_pk(pk,NULL); return keystr(pk->keyid); } const char * keystr_from_sk(PKT_secret_key *sk) { keyid_from_sk(sk,NULL); return keystr(sk->keyid); } const char * keystr_from_desc(KEYDB_SEARCH_DESC *desc) { switch(desc->mode) { case KEYDB_SEARCH_MODE_LONG_KID: case KEYDB_SEARCH_MODE_SHORT_KID: return keystr(desc->u.kid); case KEYDB_SEARCH_MODE_FPR20: { u32 keyid[2]; - keyid[0] = (unsigned char)desc->u.fpr[12] << 24 - | (unsigned char)desc->u.fpr[13] << 16 - | (unsigned char)desc->u.fpr[14] << 8 - | (unsigned char)desc->u.fpr[15] ; - keyid[1] = (unsigned char)desc->u.fpr[16] << 24 - | (unsigned char)desc->u.fpr[17] << 16 - | (unsigned char)desc->u.fpr[18] << 8 - | (unsigned char)desc->u.fpr[19] ; - + keyid[0] = buf32_to_u32 (desc->u.fpr+12); + keyid[1] = buf32_to_u32 (desc->u.fpr+16); return keystr(keyid); } case KEYDB_SEARCH_MODE_FPR16: return "?v3 fpr?"; default: BUG(); } } /**************** * Get the keyid from the secret key and put it into keyid * if this is not NULL. Return the 32 low bits of the keyid. */ u32 keyid_from_sk( PKT_secret_key *sk, u32 *keyid ) { u32 lowbits; u32 dummy_keyid[2]; if( !keyid ) keyid = dummy_keyid; if( sk->keyid[0] || sk->keyid[1] ) { keyid[0] = sk->keyid[0]; keyid[1] = sk->keyid[1]; lowbits = keyid[1]; } else if( sk->version < 4 ) { if( is_RSA(sk->pubkey_algo) ) { lowbits = pubkey_get_npkey(sk->pubkey_algo) ? mpi_get_keyid( sk->skey[0], keyid ) : 0; /* take n */ sk->keyid[0]=keyid[0]; sk->keyid[1]=keyid[1]; } else sk->keyid[0]=sk->keyid[1]=keyid[0]=keyid[1]=lowbits=0xFFFFFFFF; } else { const byte *dp; MD_HANDLE md; md = do_fingerprint_md_sk(sk); if(md) { dp = md_read( md, 0 ); - keyid[0] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ; - keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ; + keyid[0] = buf32_to_u32 (dp+12); + keyid[1] = buf32_to_u32 (dp+16); lowbits = keyid[1]; md_close(md); sk->keyid[0] = keyid[0]; sk->keyid[1] = keyid[1]; } else sk->keyid[0]=sk->keyid[1]=keyid[0]=keyid[1]=lowbits=0xFFFFFFFF; } return lowbits; } /**************** * Get the keyid from the public key and put it into keyid * if this is not NULL. Return the 32 low bits of the keyid. */ u32 keyid_from_pk( PKT_public_key *pk, u32 *keyid ) { u32 lowbits; u32 dummy_keyid[2]; if( !keyid ) keyid = dummy_keyid; if( pk->keyid[0] || pk->keyid[1] ) { keyid[0] = pk->keyid[0]; keyid[1] = pk->keyid[1]; lowbits = keyid[1]; } else if( pk->version < 4 ) { if( is_RSA(pk->pubkey_algo) ) { lowbits = pubkey_get_npkey(pk->pubkey_algo) ? mpi_get_keyid( pk->pkey[0], keyid ) : 0 ; /* from n */ pk->keyid[0] = keyid[0]; pk->keyid[1] = keyid[1]; } else pk->keyid[0]=pk->keyid[1]=keyid[0]=keyid[1]=lowbits=0xFFFFFFFF; } else { const byte *dp; MD_HANDLE md; md = do_fingerprint_md(pk); if(md) { dp = md_read( md, 0 ); - keyid[0] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ; - keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ; + keyid[0] = buf32_to_u32 (dp+12); + keyid[1] = buf32_to_u32 (dp+16); lowbits = keyid[1]; md_close(md); pk->keyid[0] = keyid[0]; pk->keyid[1] = keyid[1]; } else pk->keyid[0]=pk->keyid[1]=keyid[0]=keyid[1]=lowbits=0xFFFFFFFF; } return lowbits; } /**************** * Get the keyid from the fingerprint. This function is simple for most * keys, but has to do a keylookup for old stayle keys. */ u32 keyid_from_fingerprint( const byte *fprint, size_t fprint_len, u32 *keyid ) { u32 dummy_keyid[2]; if( !keyid ) keyid = dummy_keyid; if( fprint_len != 20 ) { /* This is special as we have to lookup the key first */ PKT_public_key pk; int rc; memset( &pk, 0, sizeof pk ); rc = get_pubkey_byfprint( &pk, fprint, fprint_len ); if( rc ) { log_error("Oops: keyid_from_fingerprint: no pubkey\n"); keyid[0] = 0; keyid[1] = 0; } else keyid_from_pk( &pk, keyid ); } else { const byte *dp = fprint; - keyid[0] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ; - keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ; + keyid[0] = buf32_to_u32 (dp+12); + keyid[1] = buf32_to_u32 (dp+16); } return keyid[1]; } u32 keyid_from_sig( PKT_signature *sig, u32 *keyid ) { if( keyid ) { keyid[0] = sig->keyid[0]; keyid[1] = sig->keyid[1]; } return sig->keyid[1]; } byte * namehash_from_uid(PKT_user_id *uid) { if(uid->namehash==NULL) { uid->namehash=xmalloc(20); if(uid->attrib_data) rmd160_hash_buffer(uid->namehash,uid->attrib_data,uid->attrib_len); else rmd160_hash_buffer(uid->namehash,uid->name,uid->len); } return uid->namehash; } /**************** * return the number of bits used in the pk */ unsigned nbits_from_pk( PKT_public_key *pk ) { return pubkey_nbits( pk->pubkey_algo, pk->pkey ); } /**************** * return the number of bits used in the sk */ unsigned nbits_from_sk( PKT_secret_key *sk ) { return pubkey_nbits( sk->pubkey_algo, sk->skey ); } static const char * mk_datestr (char *buffer, time_t atime) { struct tm *tp; if (INVALID_TIME_CHECK (atime)) strcpy (buffer, "????" "-??" "-??"); /* Mark this as invalid. */ else { tp = gmtime (&atime); sprintf (buffer,"%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday ); } return buffer; } /**************** * return a string with the creation date of the pk * Note: this is alloced in a static buffer. * Format is: yyyy-mm-dd */ const char * datestr_from_pk( PKT_public_key *pk ) { static char buffer[11+5]; time_t atime = pk->timestamp; return mk_datestr (buffer, atime); } const char * datestr_from_sk( PKT_secret_key *sk ) { static char buffer[11+5]; time_t atime = sk->timestamp; return mk_datestr (buffer, atime); } const char * datestr_from_sig( PKT_signature *sig ) { static char buffer[11+5]; time_t atime = sig->timestamp; return mk_datestr (buffer, atime); } const char * expirestr_from_pk( PKT_public_key *pk ) { static char buffer[11+5]; time_t atime; if( !pk->expiredate ) return _("never "); atime = pk->expiredate; return mk_datestr (buffer, atime); } const char * expirestr_from_sk( PKT_secret_key *sk ) { static char buffer[11+5]; time_t atime; if( !sk->expiredate ) return _("never "); atime = sk->expiredate; return mk_datestr (buffer, atime); } const char * expirestr_from_sig( PKT_signature *sig ) { static char buffer[11+5]; time_t atime; if(!sig->expiredate) return _("never "); atime=sig->expiredate; return mk_datestr (buffer, atime); } const char * revokestr_from_pk( PKT_public_key *pk ) { static char buffer[11+5]; time_t atime; if(!pk->revoked.date) return _("never "); atime=pk->revoked.date; return mk_datestr (buffer, atime); } const char * usagestr_from_pk( PKT_public_key *pk ) { static char buffer[10]; int i = 0; unsigned int use = pk->pubkey_usage; if ( use & PUBKEY_USAGE_SIG ) buffer[i++] = 'S'; if ( use & PUBKEY_USAGE_CERT ) buffer[i++] = 'C'; if ( use & PUBKEY_USAGE_ENC ) buffer[i++] = 'E'; if ( (use & PUBKEY_USAGE_AUTH) ) buffer[i++] = 'A'; while (i < 4) buffer[i++] = ' '; buffer[i] = 0; return buffer; } const char * colon_strtime (u32 t) { if (!t) return ""; if (opt.fixed_list_mode) { static char buf[15]; sprintf (buf, "%lu", (ulong)t); return buf; } return strtimestamp(t); } const char * colon_datestr_from_pk (PKT_public_key *pk) { if (opt.fixed_list_mode) { static char buf[15]; sprintf (buf, "%lu", (ulong)pk->timestamp); return buf; } return datestr_from_pk (pk); } const char * colon_datestr_from_sk (PKT_secret_key *sk) { if (opt.fixed_list_mode) { static char buf[15]; sprintf (buf, "%lu", (ulong)sk->timestamp); return buf; } return datestr_from_sk (sk); } const char * colon_datestr_from_sig (PKT_signature *sig) { if (opt.fixed_list_mode) { static char buf[15]; sprintf (buf, "%lu", (ulong)sig->timestamp); return buf; } return datestr_from_sig (sig); } const char * colon_expirestr_from_sig (PKT_signature *sig) { if(!sig->expiredate) return ""; if (opt.fixed_list_mode) { static char buf[15]; sprintf (buf, "%lu", (ulong)sig->expiredate); return buf; } return expirestr_from_sig (sig); } /**************** . * Return a byte array with the fingerprint for the given PK/SK * The length of the array is returned in ret_len. Caller must free * the array or provide an array of length MAX_FINGERPRINT_LEN. */ byte * fingerprint_from_pk( PKT_public_key *pk, byte *array, size_t *ret_len ) { byte *p, *buf; const byte *dp; size_t len; unsigned int n; if( pk->version < 4 ) { if( is_RSA(pk->pubkey_algo) ) { /* RSA in version 3 packets is special */ MD_HANDLE md; md = md_open( DIGEST_ALGO_MD5, 0); if( pubkey_get_npkey( pk->pubkey_algo ) > 1 ) { p = buf = mpi_get_buffer( pk->pkey[0], &n, NULL ); md_write( md, p, n ); xfree(buf); p = buf = mpi_get_buffer( pk->pkey[1], &n, NULL ); md_write( md, p, n ); xfree(buf); } md_final(md); if( !array ) array = xmalloc( 16 ); len = 16; memcpy(array, md_read(md, DIGEST_ALGO_MD5), 16 ); md_close(md); } else { if(!array) array=xmalloc(16); len=16; memset(array,0,16); } } else { MD_HANDLE md; md = do_fingerprint_md(pk); dp = md_read( md, 0 ); len = md_digest_length( md_get_algo( md ) ); assert( len <= MAX_FINGERPRINT_LEN ); if( !array ) array = xmalloc( len ); memcpy(array, dp, len ); - pk->keyid[0] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ; - pk->keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ; + pk->keyid[0] = buf32_to_u32 (dp+12); + pk->keyid[1] = buf32_to_u32 (dp+16); md_close(md); } *ret_len = len; return array; } byte * fingerprint_from_sk( PKT_secret_key *sk, byte *array, size_t *ret_len ) { byte *p, *buf; const char *dp; size_t len; unsigned n; if( sk->version < 4 ) { if( is_RSA(sk->pubkey_algo) ) { /* RSA in version 3 packets is special */ MD_HANDLE md; md = md_open( DIGEST_ALGO_MD5, 0); if( pubkey_get_npkey( sk->pubkey_algo ) > 1 ) { p = buf = mpi_get_buffer( sk->skey[0], &n, NULL ); md_write( md, p, n ); xfree(buf); p = buf = mpi_get_buffer( sk->skey[1], &n, NULL ); md_write( md, p, n ); xfree(buf); } md_final(md); if( !array ) array = xmalloc( 16 ); len = 16; memcpy(array, md_read(md, DIGEST_ALGO_MD5), 16 ); md_close(md); } else { if(!array) array=xmalloc(16); len=16; memset(array,0,16); } } else { MD_HANDLE md; md = do_fingerprint_md_sk(sk); if(md) { dp = md_read( md, 0 ); len = md_digest_length( md_get_algo( md ) ); assert( len <= MAX_FINGERPRINT_LEN ); if( !array ) array = xmalloc( len ); memcpy(array, dp, len ); md_close(md); } else { len=MAX_FINGERPRINT_LEN; if(!array) array=xmalloc(len); memset(array,0,len); } } *ret_len = len; return array; } diff --git a/g10/misc.c b/g10/misc.c index 60ecf96ea..2c5c6cca3 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -1,1371 +1,1360 @@ /* misc.c - miscellaneous functions * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008 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 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 #include #include #endif #ifdef HAVE_SETRLIMIT #include #include #include #endif #ifdef ENABLE_SELINUX_HACKS #include #endif #ifdef _WIN32 #include #include #include #include #ifndef CSIDL_APPDATA #define CSIDL_APPDATA 0x001a #endif #ifndef CSIDL_LOCAL_APPDATA #define CSIDL_LOCAL_APPDATA 0x001c #endif #ifndef CSIDL_FLAG_CREATE #define CSIDL_FLAG_CREATE 0x8000 #endif #include "errors.h" #include "dynload.h" #endif /*_WIN32*/ #ifdef __VMS # include #endif /* def __VMS */ #include "util.h" #include "main.h" #include "photoid.h" #include "options.h" #include "i18n.h" #include "cardglue.h" #ifdef ENABLE_SELINUX_HACKS /* A object and a global variable to keep track of files marked as secured. */ struct secured_file_item { struct secured_file_item *next; ino_t ino; dev_t dev; }; static struct secured_file_item *secured_files; #endif /*ENABLE_SELINUX_HACKS*/ #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 static int setsysinfo(unsigned long op, void *buffer, unsigned long size, int *start, void *arg, unsigned long flag) { return syscall(__NR_osf_setsysinfo, op, buffer, size, start, arg, flag); } void trap_unaligned(void) { unsigned int buf[2]; buf[0] = SSIN_UACPROC; buf[1] = UAC_SIGBUS | UAC_NOPRINT; setsysinfo(SSI_NVPAIRS, buf, 1, 0, 0, 0); } #else void trap_unaligned(void) { /* dummy */ } #endif int disable_core_dumps() { #if defined(HAVE_DOSISH_SYSTEM) || defined(__VMS) return 0; #else #ifdef HAVE_SETRLIMIT struct rlimit limit; limit.rlim_cur = 0; limit.rlim_max = 0; if( !setrlimit( RLIMIT_CORE, &limit ) ) return 0; if( errno != EINVAL && errno != ENOSYS ) log_fatal(_("can't disable core dumps: %s\n"), strerror(errno) ); #endif return 1; #endif } /* For the sake of SELinux we want to restrict access through gpg to certain files we keep under our own control. This function registers such a file and is_secured_file may then be used to check whether a file has ben registered as secured. */ void register_secured_file (const char *fname) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf; /* Note that we stop immediatley if something goes wrong here. */ if (stat (fname, &buf)) log_fatal (_("fstat of `%s' failed in %s: %s\n"), fname, "register_secured_file", strerror (errno)); /* log_debug ("registering `%s' i=%lu.%lu\n", fname, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sf=secured_files; sf; sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) return; /* Already registered. */ } sf = xmalloc (sizeof *sf); sf->ino = buf.st_ino; sf->dev = buf.st_dev; sf->next = secured_files; secured_files = sf; #endif /*ENABLE_SELINUX_HACKS*/ } /* Remove a file registerd as secure. */ void unregister_secured_file (const char *fname) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf, *sfprev; if (stat (fname, &buf)) { log_error (_("fstat of `%s' failed in %s: %s\n"), fname, "unregister_secured_file", strerror (errno)); return; } /* log_debug ("unregistering `%s' i=%lu.%lu\n", fname, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sfprev=NULL,sf=secured_files; sf; sfprev=sf, sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) { if (sfprev) sfprev->next = sf->next; else secured_files = sf->next; xfree (sf); return; } } #endif /*ENABLE_SELINUX_HACKS*/ } /* Return true if FD is corresponds to a secured file. Using -1 for FS is allowed and will return false. */ int is_secured_file (int fd) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf; if (fd == -1) return 0; /* No file descriptor so it can't be secured either. */ /* Note that we print out a error here and claim that a file is secure if something went wrong. */ if (fstat (fd, &buf)) { log_error (_("fstat(%d) failed in %s: %s\n"), fd, "is_secured_file", strerror (errno)); return 1; } /* log_debug ("is_secured_file (%d) i=%lu.%lu\n", fd, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sf=secured_files; sf; sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) return 1; /* Yes. */ } #endif /*ENABLE_SELINUX_HACKS*/ return 0; /* No. */ } /* Return true if FNAME is corresponds to a secured file. Using NULL, "" or "-" for FS is allowed and will return false. This function is used before creating a file, thus it won't fail if the file does not exist. */ int is_secured_filename (const char *fname) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf; if (iobuf_is_pipe_filename (fname) || !*fname) return 0; /* Note that we print out a error here and claim that a file is secure if something went wrong. */ if (stat (fname, &buf)) { if (errno == ENOENT || errno == EPERM || errno == EACCES) return 0; log_error (_("fstat of `%s' failed in %s: %s\n"), fname, "is_secured_filename", strerror (errno)); return 1; } /* log_debug ("is_secured_filename (%s) i=%lu.%lu\n", fname, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sf=secured_files; sf; sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) return 1; /* Yes. */ } #endif /*ENABLE_SELINUX_HACKS*/ return 0; /* No. */ } u16 checksum_u16( unsigned n ) { u16 a; a = (n >> 8) & 0xff; a += n & 0xff; return a; } u16 checksum( byte *p, unsigned n ) { u16 a; for(a=0; n; n-- ) a += *p++; return a; } u16 checksum_mpi( MPI a ) { u16 csum; byte *buffer; unsigned nbytes; unsigned nbits; buffer = mpi_get_buffer( a, &nbytes, NULL ); nbits = mpi_get_nbits(a); csum = checksum_u16( nbits ); csum += checksum( buffer, nbytes ); xfree( buffer ); return csum; } -u32 -buffer_to_u32( const byte *buffer ) -{ - unsigned long a; - a = *buffer << 24; - a |= buffer[1] << 16; - a |= buffer[2] << 8; - a |= buffer[3]; - return a; -} - void print_pubkey_algo_note( int algo ) { if(algo >= 100 && algo <= 110) { static int warn=0; if(!warn) { warn=1; log_info(_("WARNING: using experimental public key algorithm %s\n"), pubkey_algo_to_string(algo)); } } else if (algo == 20) { log_info (_("WARNING: Elgamal sign+encrypt keys are deprecated\n")); } } void print_cipher_algo_note( int algo ) { if(algo >= 100 && algo <= 110) { static int warn=0; if(!warn) { warn=1; log_info(_("WARNING: using experimental cipher algorithm %s\n"), cipher_algo_to_string(algo)); } } } void print_digest_algo_note( int algo ) { if(algo >= 100 && algo <= 110) { static int warn=0; if(!warn) { warn=1; log_info(_("WARNING: using experimental digest algorithm %s\n"), digest_algo_to_string(algo)); } } else if(algo==DIGEST_ALGO_MD5) md5_digest_warn (1); } /* Return a string which is used as a kind of process ID */ const byte * get_session_marker( size_t *rlen ) { static byte marker[SIZEOF_UNSIGNED_LONG*2]; static int initialized; if ( !initialized ) { volatile ulong aa, bb; /* we really want the uninitialized value */ ulong a, b; initialized = 1; /* also this marker is guessable it is not easy to use this * for a faked control packet because an attacker does not * have enough control about the time the verification does * take place. Of course, we can add just more random but * than we need the random generator even for verification * tasks - which does not make sense. */ a = aa ^ (ulong)getpid(); b = bb ^ (ulong)time(NULL); memcpy( marker, &a, SIZEOF_UNSIGNED_LONG ); memcpy( marker+SIZEOF_UNSIGNED_LONG, &b, SIZEOF_UNSIGNED_LONG ); } *rlen = sizeof(marker); return marker; } /**************** * Wrapper around the libgcrypt function with addional checks on * openPGP contraints for the algo ID. */ int openpgp_cipher_test_algo( int algo ) { if( algo < 0 || algo > 110 ) return G10ERR_CIPHER_ALGO; return check_cipher_algo(algo); } int openpgp_pk_test_algo( int algo, unsigned int usage_flags ) { /* Dont't allow type 20 keys unless in rfc2440 mode. */ if (!RFC2440 && algo == 20) return G10ERR_PUBKEY_ALGO; if( algo < 0 || algo > 110 ) return G10ERR_PUBKEY_ALGO; return check_pubkey_algo2( algo, usage_flags ); } int openpgp_pk_algo_usage ( int algo ) { int use = 0; /* they are hardwired in gpg 1.0 */ switch ( algo ) { case PUBKEY_ALGO_RSA: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH; break; case PUBKEY_ALGO_RSA_E: use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_RSA_S: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG; break; case PUBKEY_ALGO_ELGAMAL: /* Allow encryption with type 20 keys if RFC-2440 compliance has been selected. Signing is broken thus we won't allow this. */ if (RFC2440) use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_ELGAMAL_E: use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_DSA: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; break; default: break; } return use; } int openpgp_md_test_algo( int algo ) { if( algo < 0 || algo > 110 ) return G10ERR_DIGEST_ALGO; return check_digest_algo(algo); } /* Print a warning if the md5 digest algorithm has been used. This warning is printed only once unless SHOW is used. */ void md5_digest_warn (int show) { static int warned = 0; if (!warned || show) { log_info (_("WARNING: digest algorithm %s is deprecated\n"), digest_algo_to_string (DIGEST_ALGO_MD5)); log_info (_("please see %s for more information\n"), "https://gnupg.org/faq/weak-digest-algos.html"); warned = 1; } } void not_in_gpg1_notice (void) { static int warned = 0; if (!warned) { log_info (_("NOTE: This feature is not available in %s\n"), "GnuPG 1.x"); log_info (_("please see %s for more information\n"), "https://gnupg.org/faq/features-not-in-gnupg-1.html"); warned = 1; } } static unsigned long get_signature_count(PKT_secret_key *sk) { #ifdef ENABLE_CARD_SUPPORT if(sk && sk->is_protected && sk->protect.s2k.mode==1002) { struct agent_card_info_s info; if(agent_scd_getattr("SIG-COUNTER",&info)==0) return info.sig_counter; } #endif /* How to do this without a card? */ return 0; } /* Expand %-strings. Returns a string which must be xfreed. Returns NULL if the string cannot be expanded (too large). */ char * pct_expando(const char *string,struct expando_args *args) { const char *ch=string; int idx=0,maxlen=0,done=0; u32 pk_keyid[2]={0,0},sk_keyid[2]={0,0}; char *ret=NULL; if(args->pk) keyid_from_pk(args->pk,pk_keyid); if(args->sk) keyid_from_sk(args->sk,sk_keyid); /* This is used so that %k works in photoid command strings in --list-secret-keys (which of course has a sk, but no pk). */ if(!args->pk && args->sk) keyid_from_sk(args->sk,pk_keyid); while(*ch!='\0') { if(!done) { /* 8192 is way bigger than we'll need here */ if(maxlen>=8192) goto fail; maxlen+=1024; ret=xrealloc(ret,maxlen); } done=0; if(*ch=='%') { switch(*(ch+1)) { case 's': /* short key id */ if(idx+8sk)); idx+=strlen(&ret[idx]); done=1; } break; case 'p': /* primary pk fingerprint of a sk */ case 'f': /* pk fingerprint */ case 'g': /* sk fingerprint */ { byte array[MAX_FINGERPRINT_LEN]; size_t len; int i; if((*(ch+1))=='p' && args->sk) { if(args->sk->is_primary) fingerprint_from_sk(args->sk,array,&len); else if(args->sk->main_keyid[0] || args->sk->main_keyid[1]) { PKT_public_key *pk= xmalloc_clear(sizeof(PKT_public_key)); if(get_pubkey_fast(pk,args->sk->main_keyid)==0) fingerprint_from_pk(pk,array,&len); else memset(array,0,(len=MAX_FINGERPRINT_LEN)); free_public_key(pk); } else memset(array,0,(len=MAX_FINGERPRINT_LEN)); } else if((*(ch+1))=='f' && args->pk) fingerprint_from_pk(args->pk,array,&len); else if((*(ch+1))=='g' && args->sk) fingerprint_from_sk(args->sk,array,&len); else memset(array,0,(len=MAX_FINGERPRINT_LEN)); if(idx+(len*2)validity_info && idx+1validity_info; ret[idx]='\0'; done=1; } break; /* The text string types */ case 't': case 'T': case 'V': { const char *str=NULL; switch(*(ch+1)) { case 't': /* e.g. "jpg" */ str=image_type_to_string(args->imagetype,0); break; case 'T': /* e.g. "image/jpeg" */ str=image_type_to_string(args->imagetype,2); break; case 'V': /* e.g. "full", "expired", etc. */ str=args->validity_string; break; } if(str && idx+strlen(str)=0 && algo<=3) return 0; #else if(algo>=0 && algo<=2) return 0; #endif return G10ERR_COMPR_ALGO; } int default_cipher_algo(void) { if(opt.def_cipher_algo) return opt.def_cipher_algo; else if(opt.personal_cipher_prefs) return opt.personal_cipher_prefs[0].value; else return opt.s2k_cipher_algo; } /* There is no default_digest_algo function, but see sign.c:hash_for() */ int default_compress_algo(void) { if(opt.compress_algo!=-1) return opt.compress_algo; else if(opt.personal_compress_prefs) return opt.personal_compress_prefs[0].value; else return DEFAULT_COMPRESS_ALGO; } const char * compliance_option_string(void) { char *ver="???"; switch(opt.compliance) { case CO_GNUPG: return "--gnupg"; case CO_RFC4880: return "--openpgp"; case CO_RFC2440: return "--rfc2440"; case CO_RFC1991: return "--rfc1991"; case CO_PGP2: return "--pgp2"; case CO_PGP6: return "--pgp6"; case CO_PGP7: return "--pgp7"; case CO_PGP8: return "--pgp8"; } return ver; } void compliance_failure(void) { char *ver="???"; switch(opt.compliance) { case CO_GNUPG: ver="GnuPG"; break; case CO_RFC4880: ver="OpenPGP"; break; case CO_RFC2440: ver="OpenPGP (older)"; break; case CO_RFC1991: ver="old PGP"; break; case CO_PGP2: ver="PGP 2.x"; break; case CO_PGP6: ver="PGP 6.x"; break; case CO_PGP7: ver="PGP 7.x"; break; case CO_PGP8: ver="PGP 8.x"; break; } log_info(_("this message may not be usable by %s\n"),ver); opt.compliance=CO_GNUPG; } /* Break a string into successive option pieces. Accepts single word options and key=value argument options. */ char * optsep(char **stringp) { char *tok,*end; tok=*stringp; if(tok) { end=strpbrk(tok," ,="); if(end) { int sawequals=0; char *ptr=end; /* what we need to do now is scan along starting with *end, If the next character we see (ignoring spaces) is an = sign, then there is an argument. */ while(*ptr) { if(*ptr=='=') sawequals=1; else if(*ptr!=' ') break; ptr++; } /* There is an argument, so grab that too. At this point, ptr points to the first character of the argument. */ if(sawequals) { /* Is it a quoted argument? */ if(*ptr=='"') { ptr++; end=strchr(ptr,'"'); if(end) end++; } else end=strpbrk(ptr," ,"); } if(end && *end) { *end='\0'; *stringp=end+1; } else *stringp=NULL; } else *stringp=NULL; } return tok; } /* Breaks an option value into key and value. Returns NULL if there is no value. Note that "string" is modified to remove the =value part. */ char * argsplit(char *string) { char *equals,*arg=NULL; equals=strchr(string,'='); if(equals) { char *quote,*space; *equals='\0'; arg=equals+1; /* Quoted arg? */ quote=strchr(arg,'"'); if(quote) { arg=quote+1; quote=strchr(arg,'"'); if(quote) *quote='\0'; } else { size_t spaces; /* Trim leading spaces off of the arg */ spaces=strspn(arg," "); arg+=spaces; } /* Trim tailing spaces off of the tag */ space=strchr(string,' '); if(space) *space='\0'; } return arg; } /* Return the length of the initial token, leaving off any argument. */ static size_t optlen(const char *s) { char *end=strpbrk(s," ="); if(end) return end-s; else return strlen(s); } int parse_options(char *str,unsigned int *options, struct parse_options *opts,int noisy) { char *tok; if (str && !strcmp (str, "help")) { int i,maxlen=0; /* Figure out the longest option name so we can line these up neatly. */ for(i=0;opts[i].name;i++) if(opts[i].help && maxlen= 0) { char *tmp = xmalloc (strlen (path) + 6 +1); strcpy (stpcpy (tmp, path), "\\gnupg"); dir = tmp; /* Try to create the directory if it does not yet exists. */ if (access (dir, F_OK)) CreateDirectory (dir, NULL); } } #endif /*HAVE_W32_SYSTEM*/ if (!dir || !*dir) dir = GNUPG_HOMEDIR; return dir; } /* Return the name of the libexec directory. The name is allocated in a static area on the first use. This function won't fail. */ const char * get_libexecdir (void) { #ifdef HAVE_W32_SYSTEM static int got_dir; static char dir[MAX_PATH+5]; if (!got_dir) { char *p; if ( !GetModuleFileName ( NULL, dir, MAX_PATH) ) { log_debug ("GetModuleFileName failed: %s\n", w32_strerror (0)); *dir = 0; } got_dir = 1; p = strrchr (dir, DIRSEP_C); if (p) *p = 0; else { log_debug ("bad filename `%s' returned for this process\n", dir); *dir = 0; } } if (*dir) return dir; /* Fallback to the hardwired value. */ #endif /*HAVE_W32_SYSTEM*/ return GNUPG_LIBEXECDIR; } /* Similar to access(2), but uses PATH to find the file. (2006-07-08 SMS: See "vmslib/vms.c" for a VMS-specific replacement function) */ #ifndef __VMS int path_access(const char *file,int mode) { char *envpath; int ret=-1; envpath=getenv("PATH"); if(!envpath #ifdef HAVE_DRIVE_LETTERS || (((file[0]>='A' && file[0]<='Z') || (file[0]>='a' && file[0]<='z')) && file[1]==':') #else || file[0]=='/' #endif ) return access(file,mode); else { /* At least as large as, but most often larger than we need. */ char *buffer=xmalloc(strlen(envpath)+1+strlen(file)+1); char *split,*item,*path=xstrdup(envpath); split=path; while((item=strsep(&split,PATHSEP_S))) { strcpy(buffer,item); strcat(buffer,"/"); strcat(buffer,file); ret=access(buffer,mode); if(ret==0) break; } xfree(path); xfree(buffer); } return ret; } #endif /*ndef __VMS*/ diff --git a/g10/parse-packet.c b/g10/parse-packet.c index e7e923b78..862ec6e7f 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -1,2537 +1,2538 @@ /* parse-packet.c - read packets * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008 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 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include "packet.h" #include "iobuf.h" #include "mpi.h" #include "util.h" #include "cipher.h" #include "memory.h" #include "filter.h" #include "photoid.h" #include "options.h" #include "main.h" #include "i18n.h" +#include "host2net.h" #ifndef MAX_EXTERN_MPI_BITS #define MAX_EXTERN_MPI_BITS 16384 #endif /* Maximum length of packets to avoid excessive memory allocation. */ #define MAX_KEY_PACKET_LENGTH (256 * 1024) #define MAX_UID_PACKET_LENGTH ( 2 * 1024) #define MAX_COMMENT_PACKET_LENGTH ( 64 * 1024) #define MAX_ATTR_PACKET_LENGTH ( 16 * 1024*1024) static int mpi_print_mode; static int list_mode; static FILE *listfp; static int parse( IOBUF inp, PACKET *pkt, int onlykeypkts, off_t *retpos, int *skip, IOBUF out, int do_skip #ifdef DEBUG_PARSE_PACKET ,const char *dbg_w, const char *dbg_f, int dbg_l #endif ); static int copy_packet( IOBUF inp, IOBUF out, int pkttype, unsigned long pktlen, int partial ); static void skip_packet( IOBUF inp, int pkttype, unsigned long pktlen, int partial ); static void *read_rest( IOBUF inp, size_t pktlen, int partial ); static int parse_marker( IOBUF inp, int pkttype, unsigned long pktlen ); static int parse_symkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ); static int parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ); static int parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen, PKT_onepass_sig *ops ); static int parse_key( IOBUF inp, int pkttype, unsigned long pktlen, byte *hdr, int hdrlen, PACKET *packet ); static int parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ); static int parse_attribute( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ); static int parse_comment( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ); static void parse_trust( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ); static int parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet, int new_ctb, int partial); static int parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet, int new_ctb ); static int parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet, int new_ctb, int partial); static int parse_mdc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet, int new_ctb); static int parse_gpg_control( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet, int partial ); static unsigned short read_16(IOBUF inp) { unsigned short a; - a = iobuf_get_noeof(inp) << 8; + a = (unsigned short)iobuf_get_noeof(inp) << 8; a |= iobuf_get_noeof(inp); return a; } static unsigned long read_32(IOBUF inp) { unsigned long a; - a = iobuf_get_noeof(inp) << 24; + a = (unsigned long)iobuf_get_noeof(inp) << 24; a |= iobuf_get_noeof(inp) << 16; a |= iobuf_get_noeof(inp) << 8; a |= iobuf_get_noeof(inp); return a; } int set_packet_list_mode( int mode ) { int old = list_mode; list_mode = mode; mpi_print_mode = DBG_MPI; /* We use stdout print only if invoked by the --list-packets command but switch to stderr in all otehr cases. This breaks the previous behaviour but that seems to be more of a bug than intentional. I don't believe that any application makes use of this long standing annoying way of printing to stdout except when doing a --list-packets. If this assumption fails, it will be easy to add an option for the listing stream. Note that we initialize it only once; mainly because some code may switch the option value later back to 1 and we want to have all output to the same stream. Using stderr is not actually very clean because it bypasses the logging code but it is a special thing anyay. I am not sure whether using log_stream() would be better. Perhaps we should enable the list mdoe only with a special option. */ if (!listfp) listfp = opt.list_packets == 2 ? stdout : stderr; return old; } static void unknown_pubkey_warning( int algo ) { static byte unknown_pubkey_algos[256]; algo &= 0xff; if( !unknown_pubkey_algos[algo] ) { if( opt.verbose ) log_info(_("can't handle public key algorithm %d\n"), algo ); unknown_pubkey_algos[algo] = 1; } } /**************** * Parse a Packet and return it in packet * Returns: 0 := valid packet in pkt * -1 := no more packets * >0 := error * Note: The function may return an error and a partly valid packet; * caller must free this packet. */ #ifdef DEBUG_PARSE_PACKET int dbg_parse_packet( IOBUF inp, PACKET *pkt, const char *dbg_f, int dbg_l ) { int skip, rc; do { rc = parse( inp, pkt, 0, NULL, &skip, NULL, 0, "parse", dbg_f, dbg_l ); } while( skip ); return rc; } #else int parse_packet( IOBUF inp, PACKET *pkt ) { int skip, rc; do { rc = parse( inp, pkt, 0, NULL, &skip, NULL, 0 ); } while( skip ); return rc; } #endif /**************** * Like parse packet, but only return secret or public (sub)key packets. */ #ifdef DEBUG_PARSE_PACKET int dbg_search_packet( IOBUF inp, PACKET *pkt, off_t *retpos, int with_uid, const char *dbg_f, int dbg_l ) { int skip, rc; do { rc = parse( inp, pkt, with_uid?2:1, retpos, &skip, NULL, 0, "search", dbg_f, dbg_l ); } while( skip ); return rc; } #else int search_packet( IOBUF inp, PACKET *pkt, off_t *retpos, int with_uid ) { int skip, rc; do { rc = parse( inp, pkt, with_uid?2:1, retpos, &skip, NULL, 0 ); } while( skip ); return rc; } #endif /**************** * Copy all packets from INP to OUT, thereby removing unused spaces. */ #ifdef DEBUG_PARSE_PACKET int dbg_copy_all_packets( IOBUF inp, IOBUF out, const char *dbg_f, int dbg_l ) { PACKET pkt; int skip, rc=0; do { init_packet(&pkt); } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0, "copy", dbg_f, dbg_l ))); return rc; } #else int copy_all_packets( IOBUF inp, IOBUF out ) { PACKET pkt; int skip, rc=0; do { init_packet(&pkt); } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0 ))); return rc; } #endif /**************** * Copy some packets from INP to OUT, thereby removing unused spaces. * Stop at offset STOPoff (i.e. don't copy packets at this or later offsets) */ #ifdef DEBUG_PARSE_PACKET int dbg_copy_some_packets( IOBUF inp, IOBUF out, off_t stopoff, const char *dbg_f, int dbg_l ) { PACKET pkt; int skip, rc=0; do { if( iobuf_tell(inp) >= stopoff ) return 0; init_packet(&pkt); } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0, "some", dbg_f, dbg_l )) ); return rc; } #else int copy_some_packets( IOBUF inp, IOBUF out, off_t stopoff ) { PACKET pkt; int skip, rc=0; do { if( iobuf_tell(inp) >= stopoff ) return 0; init_packet(&pkt); } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0 )) ); return rc; } #endif /**************** * Skip over N packets */ #ifdef DEBUG_PARSE_PACKET int dbg_skip_some_packets( IOBUF inp, unsigned n, const char *dbg_f, int dbg_l ) { int skip, rc=0; PACKET pkt; for( ;n && !rc; n--) { init_packet(&pkt); rc = parse( inp, &pkt, 0, NULL, &skip, NULL, 1, "skip", dbg_f, dbg_l ); } return rc; } #else int skip_some_packets( IOBUF inp, unsigned n ) { int skip, rc=0; PACKET pkt; for( ;n && !rc; n--) { init_packet(&pkt); rc = parse( inp, &pkt, 0, NULL, &skip, NULL, 1 ); } return rc; } #endif /**************** * Parse packet. Set the variable skip points to 1 if the packet * should be skipped; this is the case if either ONLYKEYPKTS is set * and the parsed packet isn't one or the * packet-type is 0, indicating deleted stuff. * if OUT is not NULL, a special copymode is used. */ static int parse( IOBUF inp, PACKET *pkt, int onlykeypkts, off_t *retpos, int *skip, IOBUF out, int do_skip #ifdef DEBUG_PARSE_PACKET ,const char *dbg_w, const char *dbg_f, int dbg_l #endif ) { int rc=0, c, ctb, pkttype, lenbytes; unsigned long pktlen; byte hdr[8]; int hdrlen; int new_ctb = 0, partial=0; int with_uid = (onlykeypkts == 2); *skip = 0; assert( !pkt->pkt.generic ); if( retpos ) *retpos = iobuf_tell(inp); if( (ctb = iobuf_get(inp)) == -1 ) { rc = -1; goto leave; } hdrlen=0; hdr[hdrlen++] = ctb; if( !(ctb & 0x80) ) { log_error("%s: invalid packet (ctb=%02x)\n", iobuf_where(inp), ctb ); rc = G10ERR_INVALID_PACKET; goto leave; } pktlen = 0; new_ctb = !!(ctb & 0x40); if( new_ctb ) { pkttype = ctb & 0x3f; if( (c = iobuf_get(inp)) == -1 ) { log_error("%s: 1st length byte missing\n", iobuf_where(inp) ); rc = G10ERR_INVALID_PACKET; goto leave; } /* The follwing code has been here for ages (2002-08-30) but it is clearly wrong: For example passing a 0 as second argument to iobuf_set_partial_block_mode stops the partial block mode which we definitely do not want. Also all values < 224 or 255 are not valid. Let's disable it and put PKT_COMPRESSED into the list of allowed packets with partial header until someone complains. */ /* if (pkttype == PKT_COMPRESSED) { */ /* iobuf_set_partial_block_mode(inp, c & 0xff); */ /* pktlen = 0;/\* to indicate partial length *\/ */ /* partial=1; */ /* } */ /* else { */ { hdr[hdrlen++] = c; if( c < 192 ) pktlen = c; else if( c < 224 ) { pktlen = (c - 192) * 256; if( (c = iobuf_get(inp)) == -1 ) { log_error("%s: 2nd length byte missing\n", iobuf_where(inp) ); rc = G10ERR_INVALID_PACKET; goto leave; } hdr[hdrlen++] = c; pktlen += c + 192; } else if( c == 255 ) { - pktlen = (hdr[hdrlen++] = iobuf_get_noeof(inp)) << 24; + pktlen = + (unsigned long)(hdr[hdrlen++] = iobuf_get_noeof(inp)) << 24; pktlen |= (hdr[hdrlen++] = iobuf_get_noeof(inp)) << 16; pktlen |= (hdr[hdrlen++] = iobuf_get_noeof(inp)) << 8; if( (c = iobuf_get(inp)) == -1 ) { log_error("%s: 4 byte length invalid\n", iobuf_where(inp) ); rc = G10ERR_INVALID_PACKET; goto leave; } pktlen |= (hdr[hdrlen++] = c ); } else { /* Partial body length. */ switch (pkttype) { case PKT_PLAINTEXT: case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: case PKT_COMPRESSED: iobuf_set_partial_block_mode (inp, (c & 0xff)); pktlen = 0; /* Indicate partial length. */ partial= 1; break; default: log_error ("%s: partial length for invalid" " packet type %d\n", iobuf_where(inp),pkttype); rc = G10ERR_INVALID_PACKET; goto leave; } } } } else { pkttype = (ctb>>2)&0xf; lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3)); if( !lenbytes ) { pktlen = 0; /* don't know the value */ /* This isn't really partial, but we can treat it the same in a "read until the end" sort of way. */ partial=1; if(pkttype!=PKT_ENCRYPTED && pkttype!=PKT_PLAINTEXT && pkttype!=PKT_COMPRESSED) { log_error ("%s: indeterminate length for invalid" " packet type %d\n", iobuf_where(inp), pkttype ); rc = G10ERR_INVALID_PACKET; goto leave; } } else { for( ; lenbytes; lenbytes-- ) { pktlen <<= 8; pktlen |= hdr[hdrlen++] = iobuf_get_noeof(inp); } } } if (pktlen == (unsigned long)(-1)) { /* With some probability this is caused by a problem in the * the uncompressing layer - in some error cases it just loops * and spits out 0xff bytes. */ log_error ("%s: garbled packet detected\n", iobuf_where(inp) ); g10_exit (2); } if( out && pkttype ) { if( iobuf_write( out, hdr, hdrlen ) == -1 ) rc = G10ERR_WRITE_FILE; else rc = copy_packet(inp, out, pkttype, pktlen, partial ); goto leave; } if (with_uid && pkttype == PKT_USER_ID) ; else if( do_skip || !pkttype || (onlykeypkts && pkttype != PKT_PUBLIC_SUBKEY && pkttype != PKT_PUBLIC_KEY && pkttype != PKT_SECRET_SUBKEY && pkttype != PKT_SECRET_KEY ) ) { iobuf_skip_rest(inp, pktlen, partial); *skip = 1; rc = 0; goto leave; } if( DBG_PACKET ) { #ifdef DEBUG_PARSE_PACKET log_debug("parse_packet(iob=%d): type=%d length=%lu%s (%s.%s.%d)\n", iobuf_id(inp), pkttype, pktlen, new_ctb?" (new_ctb)":"", dbg_w, dbg_f, dbg_l ); #else log_debug("parse_packet(iob=%d): type=%d length=%lu%s\n", iobuf_id(inp), pkttype, pktlen, new_ctb?" (new_ctb)":"" ); #endif } pkt->pkttype = pkttype; rc = G10ERR_UNKNOWN_PACKET; /* default error */ switch( pkttype ) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: pkt->pkt.public_key = xmalloc_clear(sizeof *pkt->pkt.public_key ); rc = parse_key(inp, pkttype, pktlen, hdr, hdrlen, pkt ); break; case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: pkt->pkt.secret_key = xmalloc_clear(sizeof *pkt->pkt.secret_key ); rc = parse_key(inp, pkttype, pktlen, hdr, hdrlen, pkt ); break; case PKT_SYMKEY_ENC: rc = parse_symkeyenc( inp, pkttype, pktlen, pkt ); break; case PKT_PUBKEY_ENC: rc = parse_pubkeyenc(inp, pkttype, pktlen, pkt ); break; case PKT_SIGNATURE: pkt->pkt.signature = xmalloc_clear(sizeof *pkt->pkt.signature ); rc = parse_signature(inp, pkttype, pktlen, pkt->pkt.signature ); break; case PKT_ONEPASS_SIG: pkt->pkt.onepass_sig = xmalloc_clear(sizeof *pkt->pkt.onepass_sig ); rc = parse_onepass_sig(inp, pkttype, pktlen, pkt->pkt.onepass_sig ); break; case PKT_USER_ID: rc = parse_user_id(inp, pkttype, pktlen, pkt ); break; case PKT_ATTRIBUTE: pkt->pkttype = pkttype = PKT_USER_ID; /* we store it in the userID */ rc = parse_attribute(inp, pkttype, pktlen, pkt); break; case PKT_OLD_COMMENT: case PKT_COMMENT: rc = parse_comment(inp, pkttype, pktlen, pkt); break; case PKT_RING_TRUST: parse_trust(inp, pkttype, pktlen, pkt); rc = 0; break; case PKT_PLAINTEXT: rc = parse_plaintext(inp, pkttype, pktlen, pkt, new_ctb, partial ); break; case PKT_COMPRESSED: rc = parse_compressed(inp, pkttype, pktlen, pkt, new_ctb ); break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: rc = parse_encrypted(inp, pkttype, pktlen, pkt, new_ctb, partial ); break; case PKT_MDC: rc = parse_mdc(inp, pkttype, pktlen, pkt, new_ctb ); break; case PKT_GPG_CONTROL: rc = parse_gpg_control(inp, pkttype, pktlen, pkt, partial ); break; case PKT_MARKER: rc = parse_marker(inp,pkttype,pktlen); break; default: skip_packet(inp, pkttype, pktlen, partial); break; } leave: if( !rc && iobuf_error(inp) ) rc = G10ERR_INV_KEYRING; return rc; } static void dump_hex_line( int c, int *i ) { if( *i && !(*i%8) ) { if( *i && !(*i%24) ) fprintf (listfp, "\n%4d:", *i ); else putc (' ', listfp); } if( c == -1 ) fprintf (listfp, " EOF" ); else fprintf (listfp, " %02x", c ); ++*i; } static int copy_packet( IOBUF inp, IOBUF out, int pkttype, unsigned long pktlen, int partial ) { int n; char buf[100]; if( partial ) { while( (n = iobuf_read( inp, buf, 100 )) != -1 ) if( iobuf_write(out, buf, n ) ) return G10ERR_WRITE_FILE; /* write error */ } else if( !pktlen && pkttype == PKT_COMPRESSED ) { log_debug("copy_packet: compressed!\n"); /* compressed packet, copy till EOF */ while( (n = iobuf_read( inp, buf, 100 )) != -1 ) if( iobuf_write(out, buf, n ) ) return G10ERR_WRITE_FILE; /* write error */ } else { for( ; pktlen; pktlen -= n ) { n = pktlen > 100 ? 100 : pktlen; n = iobuf_read( inp, buf, n ); if( n == -1 ) return G10ERR_READ_FILE; if( iobuf_write(out, buf, n ) ) return G10ERR_WRITE_FILE; /* write error */ } } return 0; } static void skip_packet( IOBUF inp, int pkttype, unsigned long pktlen, int partial ) { if( list_mode ) { fprintf (listfp, ":unknown packet: type %2d, length %lu\n", pkttype, pktlen); if( pkttype ) { int c, i=0 ; fputs("dump:", listfp ); if( partial ) { while( (c=iobuf_get(inp)) != -1 ) dump_hex_line(c, &i); } else { for( ; pktlen; pktlen-- ) { dump_hex_line ((c=iobuf_get(inp)), &i); if (c==-1) break; } } putc ('\n', listfp); return; } } iobuf_skip_rest(inp,pktlen,partial); } static void * read_rest( IOBUF inp, size_t pktlen, int partial ) { byte *p; int i; if( partial ) { log_error("read_rest: can't store stream data\n"); p = NULL; } else { p = xmalloc( pktlen ); for(i=0; pktlen; pktlen--, i++ ) p[i] = iobuf_get(inp); } return p; } static int parse_marker( IOBUF inp, int pkttype, unsigned long pktlen ) { if(pktlen!=3) goto fail; if(iobuf_get(inp)!='P') { pktlen--; goto fail; } if(iobuf_get(inp)!='G') { pktlen--; goto fail; } if(iobuf_get(inp)!='P') { pktlen--; goto fail; } if(list_mode) fputs(":marker packet: PGP\n", listfp ); return 0; fail: log_error("invalid marker packet\n"); iobuf_skip_rest(inp,pktlen,0); return G10ERR_INVALID_PACKET; } static int parse_symkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) { PKT_symkey_enc *k; int rc = 0; int i, version, s2kmode, cipher_algo, hash_algo, seskeylen, minlen; if( pktlen < 4 ) { log_error("packet(%d) too short\n", pkttype); rc = G10ERR_INVALID_PACKET; goto leave; } version = iobuf_get_noeof(inp); pktlen--; if( version != 4 ) { log_error("packet(%d) with unknown version %d\n", pkttype, version); rc = G10ERR_INVALID_PACKET; goto leave; } if( pktlen > 200 ) { /* (we encode the seskeylen in a byte) */ log_error("packet(%d) too large\n", pkttype); rc = G10ERR_INVALID_PACKET; goto leave; } cipher_algo = iobuf_get_noeof(inp); pktlen--; s2kmode = iobuf_get_noeof(inp); pktlen--; hash_algo = iobuf_get_noeof(inp); pktlen--; switch( s2kmode ) { case 0: /* simple s2k */ minlen = 0; break; case 1: /* salted s2k */ minlen = 8; break; case 3: /* iterated+salted s2k */ minlen = 9; break; default: log_error("unknown S2K %d\n", s2kmode ); goto leave; } if( minlen > pktlen ) { log_error("packet with S2K %d too short\n", s2kmode ); rc = G10ERR_INVALID_PACKET; goto leave; } seskeylen = pktlen - minlen; k = packet->pkt.symkey_enc = xmalloc_clear( sizeof *packet->pkt.symkey_enc + seskeylen - 1 ); k->version = version; k->cipher_algo = cipher_algo; k->s2k.mode = s2kmode; k->s2k.hash_algo = hash_algo; if( s2kmode == 1 || s2kmode == 3 ) { for(i=0; i < 8 && pktlen; i++, pktlen-- ) k->s2k.salt[i] = iobuf_get_noeof(inp); } if( s2kmode == 3 ) { k->s2k.count = iobuf_get(inp); pktlen--; } k->seskeylen = seskeylen; if(k->seskeylen) { for(i=0; i < seskeylen && pktlen; i++, pktlen-- ) k->seskey[i] = iobuf_get_noeof(inp); /* What we're watching out for here is a session key decryptor with no salt. The RFC says that using salt for this is a MUST. */ if(s2kmode!=1 && s2kmode!=3) log_info(_("WARNING: potentially insecure symmetrically" " encrypted session key\n")); } assert( !pktlen ); if( list_mode ) { fprintf (listfp, ":symkey enc packet: version %d, cipher %d, s2k %d, hash %d", version, cipher_algo, s2kmode, hash_algo); if(seskeylen) fprintf (listfp, ", seskey %d bits",(seskeylen-1)*8); fprintf (listfp, "\n"); if( s2kmode == 1 || s2kmode == 3 ) { fprintf (listfp, "\tsalt "); for(i=0; i < 8; i++ ) fprintf (listfp, "%02x", k->s2k.salt[i]); if( s2kmode == 3 ) fprintf (listfp, ", count %lu (%lu)", S2K_DECODE_COUNT((ulong)k->s2k.count), (ulong)k->s2k.count ); fprintf (listfp, "\n"); } } leave: iobuf_skip_rest(inp, pktlen, 0); return rc; } static int parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) { unsigned int n; int rc = 0; int i, ndata; PKT_pubkey_enc *k; k = packet->pkt.pubkey_enc = xmalloc_clear(sizeof *packet->pkt.pubkey_enc); if( pktlen < 12 ) { log_error("packet(%d) too short\n", pkttype); rc = G10ERR_INVALID_PACKET; goto leave; } k->version = iobuf_get_noeof(inp); pktlen--; if( k->version != 2 && k->version != 3 ) { log_error("packet(%d) with unknown version %d\n", pkttype, k->version); rc = G10ERR_INVALID_PACKET; goto leave; } k->keyid[0] = read_32(inp); pktlen -= 4; k->keyid[1] = read_32(inp); pktlen -= 4; k->pubkey_algo = iobuf_get_noeof(inp); pktlen--; k->throw_keyid = 0; /* only used as flag for build_packet */ if( list_mode ) fprintf (listfp, ":pubkey enc packet: version %d, algo %d, keyid %08lX%08lX\n", k->version, k->pubkey_algo, (ulong)k->keyid[0], (ulong)k->keyid[1]); ndata = pubkey_get_nenc(k->pubkey_algo); if( !ndata ) { if( list_mode ) fprintf (listfp, "\tunsupported algorithm %d\n", k->pubkey_algo ); unknown_pubkey_warning( k->pubkey_algo ); k->data[0] = NULL; /* no need to store the encrypted data */ } else { for( i=0; i < ndata; i++ ) { n = pktlen; k->data[i] = mpi_read(inp, &n, 0); pktlen -=n; if( list_mode ) { fprintf (listfp, "\tdata: "); mpi_print(listfp, k->data[i], mpi_print_mode ); putc ('\n', listfp); } if (!k->data[i]) rc = G10ERR_INVALID_PACKET; } } leave: iobuf_skip_rest(inp, pktlen, 0); return rc; } static void dump_sig_subpkt( int hashed, int type, int critical, const byte *buffer, size_t buflen, size_t length ) { const char *p=NULL; int i; /* The CERT has warning out with explains how to use GNUPG to * detect the ARRs - we print our old message here when it is a faked * ARR and add an additional notice */ if ( type == SIGSUBPKT_ARR && !hashed ) { fprintf (listfp, "\tsubpkt %d len %u (additional recipient request)\n" "WARNING: PGP versions > 5.0 and < 6.5.8 will automagically " "encrypt to this key and thereby reveal the plaintext to " "the owner of this ARR key. Detailed info follows:\n", type, (unsigned)length ); } buffer++; length--; fprintf (listfp, "\t%s%ssubpkt %d len %u (", /*)*/ critical ? "critical ":"", hashed ? "hashed ":"", type, (unsigned)length ); if( length > buflen ) { fprintf (listfp, "too short: buffer is only %u)\n", (unsigned)buflen ); return; } switch( type ) { case SIGSUBPKT_SIG_CREATED: if( length >= 4 ) - fprintf (listfp, "sig created %s", strtimestamp( buffer_to_u32(buffer) ) ); + fprintf (listfp, "sig created %s", + strtimestamp (buf32_to_u32(buffer)) ); break; case SIGSUBPKT_SIG_EXPIRE: if( length >= 4 ) { - if(buffer_to_u32(buffer)) + if(buf32_to_u32(buffer)) fprintf (listfp, "sig expires after %s", - strtimevalue( buffer_to_u32(buffer) ) ); + strtimevalue( buf32_to_u32(buffer) ) ); else fprintf (listfp, "sig does not expire"); } break; case SIGSUBPKT_EXPORTABLE: if( length ) fprintf (listfp, "%sexportable", *buffer? "":"not "); break; case SIGSUBPKT_TRUST: if(length!=2) p="[invalid trust subpacket]"; else fprintf (listfp, "trust signature of depth %d, value %d", buffer[0],buffer[1]); break; case SIGSUBPKT_REGEXP: if(!length) p="[invalid regexp subpacket]"; else { fprintf (listfp, "regular expression: \""); print_string (listfp, buffer, length, '\"'); p = "\""; } break; case SIGSUBPKT_REVOCABLE: if( length ) fprintf (listfp, "%srevocable", *buffer? "":"not "); break; case SIGSUBPKT_KEY_EXPIRE: if( length >= 4 ) { - if(buffer_to_u32(buffer)) + if(buf32_to_u32(buffer)) fprintf (listfp, "key expires after %s", - strtimevalue( buffer_to_u32(buffer) ) ); + strtimevalue( buf32_to_u32(buffer) ) ); else fprintf (listfp, "key does not expire"); } break; case SIGSUBPKT_PREF_SYM: fputs("pref-sym-algos:", listfp ); for( i=0; i < length; i++ ) fprintf (listfp, " %d", buffer[i] ); break; case SIGSUBPKT_REV_KEY: fputs("revocation key: ", listfp ); if( length < 22 ) p = "[too short]"; else { fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1] ); for( i=2; i < length; i++ ) fprintf (listfp, "%02X", buffer[i] ); } break; case SIGSUBPKT_ISSUER: if( length >= 8 ) fprintf (listfp, "issuer key ID %08lX%08lX", - (ulong)buffer_to_u32(buffer), - (ulong)buffer_to_u32(buffer+4) ); + buf32_to_ulong (buffer), + buf32_to_ulong (buffer+4)); break; case SIGSUBPKT_NOTATION: { fputs("notation: ", listfp ); if( length < 8 ) p = "[too short]"; else { const byte *s = buffer; size_t n1, n2; n1 = (s[4] << 8) | s[5]; n2 = (s[6] << 8) | s[7]; s += 8; if( 8+n1+n2 != length ) p = "[error]"; else { print_string( listfp, s, n1, ')' ); putc( '=', listfp ); if( *buffer & 0x80 ) print_string( listfp, s+n1, n2, ')' ); else p = "[not human readable]"; } } } break; case SIGSUBPKT_PREF_HASH: fputs("pref-hash-algos:", listfp ); for( i=0; i < length; i++ ) fprintf (listfp, " %d", buffer[i] ); break; case SIGSUBPKT_PREF_COMPR: fputs("pref-zip-algos:", listfp ); for( i=0; i < length; i++ ) fprintf (listfp, " %d", buffer[i] ); break; case SIGSUBPKT_KS_FLAGS: fputs("key server preferences:",listfp); for(i=0;i=100 && type<=110) p="experimental / private subpacket"; else p = "?"; break; } fprintf (listfp, "%s)\n", p? p: ""); } /**************** * Returns: >= 0 use this offset into buffer * -1 explicitly reject returning this type * -2 subpacket too short */ int parse_one_sig_subpkt( const byte *buffer, size_t n, int type ) { switch( type ) { case SIGSUBPKT_REV_KEY: if(n < 22) break; return 0; case SIGSUBPKT_SIG_CREATED: case SIGSUBPKT_SIG_EXPIRE: case SIGSUBPKT_KEY_EXPIRE: if( n < 4 ) break; return 0; case SIGSUBPKT_KEY_FLAGS: case SIGSUBPKT_KS_FLAGS: case SIGSUBPKT_PREF_SYM: case SIGSUBPKT_PREF_HASH: case SIGSUBPKT_PREF_COMPR: case SIGSUBPKT_POLICY: case SIGSUBPKT_PREF_KS: case SIGSUBPKT_FEATURES: case SIGSUBPKT_REGEXP: return 0; case SIGSUBPKT_SIGNATURE: case SIGSUBPKT_EXPORTABLE: case SIGSUBPKT_REVOCABLE: case SIGSUBPKT_REVOC_REASON: if( !n ) break; return 0; case SIGSUBPKT_ISSUER: /* issuer key ID */ if( n < 8 ) break; return 0; case SIGSUBPKT_NOTATION: /* minimum length needed, and the subpacket must be well-formed where the name length and value length all fit inside the packet. */ if(n<8 || 8+((buffer[4]<<8)|buffer[5])+((buffer[6]<<8)|buffer[7]) != n) break; return 0; case SIGSUBPKT_PRIMARY_UID: if ( n != 1 ) break; return 0; case SIGSUBPKT_TRUST: if ( n != 2 ) break; return 0; default: return 0; } return -2; } /* Not many critical notations we understand yet... */ static int can_handle_critical_notation(const byte *name,size_t len) { if(len==32 && memcmp(name,"preferred-email-encoding@pgp.com",32)==0) return 1; if(len==21 && memcmp(name,"pka-address@gnupg.org",21)==0) return 1; return 0; } static int can_handle_critical( const byte *buffer, size_t n, int type ) { switch( type ) { case SIGSUBPKT_NOTATION: if (n >= 8) { size_t notation_len = ((buffer[4] << 8) | buffer[5]); if (n - 8 >= notation_len) return can_handle_critical_notation (buffer + 8, notation_len); } return 0; case SIGSUBPKT_SIGNATURE: case SIGSUBPKT_SIG_CREATED: case SIGSUBPKT_SIG_EXPIRE: case SIGSUBPKT_KEY_EXPIRE: case SIGSUBPKT_EXPORTABLE: case SIGSUBPKT_REVOCABLE: case SIGSUBPKT_REV_KEY: case SIGSUBPKT_ISSUER:/* issuer key ID */ case SIGSUBPKT_PREF_SYM: case SIGSUBPKT_PREF_HASH: case SIGSUBPKT_PREF_COMPR: case SIGSUBPKT_KEY_FLAGS: case SIGSUBPKT_PRIMARY_UID: case SIGSUBPKT_FEATURES: case SIGSUBPKT_TRUST: case SIGSUBPKT_REGEXP: /* Is it enough to show the policy or keyserver? */ case SIGSUBPKT_POLICY: case SIGSUBPKT_PREF_KS: return 1; default: return 0; } } const byte * enum_sig_subpkt( const subpktarea_t *pktbuf, sigsubpkttype_t reqtype, size_t *ret_n, int *start, int *critical ) { const byte *buffer; int buflen; int type; int critical_dummy; int offset; size_t n; int seq = 0; int reqseq = start? *start: 0; if(!critical) critical=&critical_dummy; if( !pktbuf || reqseq == -1 ) { /* return some value different from NULL to indicate that * there is no critical bit we do not understand. The caller * will never use the value. Yes I know, it is an ugly hack */ return reqtype == SIGSUBPKT_TEST_CRITICAL? (const byte*)&pktbuf : NULL; } buffer = pktbuf->data; buflen = pktbuf->len; while( buflen ) { n = *buffer++; buflen--; if( n == 255 ) { /* 4 byte length header */ if( buflen < 4 ) goto too_short; - n = (buffer[0] << 24) | (buffer[1] << 16) - | (buffer[2] << 8) | buffer[3]; + n = buf32_to_size_t (buffer); buffer += 4; buflen -= 4; } else if( n >= 192 ) { /* 2 byte special encoded length header */ if( buflen < 2 ) goto too_short; n = (( n - 192 ) << 8) + *buffer + 192; buffer++; buflen--; } if( buflen < n ) goto too_short; type = *buffer; if( type & 0x80 ) { type &= 0x7f; *critical = 1; } else *critical = 0; if( !(++seq > reqseq) ) ; else if( reqtype == SIGSUBPKT_TEST_CRITICAL ) { if( *critical ) { if( n-1 > buflen+1 ) goto too_short; if( !can_handle_critical(buffer+1, n-1, type ) ) { if(opt.verbose) log_info(_("subpacket of type %d has " "critical bit set\n"),type); if( start ) *start = seq; return NULL; /* this is an error */ } } } else if( reqtype < 0 ) /* list packets */ dump_sig_subpkt( reqtype == SIGSUBPKT_LIST_HASHED, type, *critical, buffer, buflen, n ); else if( type == reqtype ) { /* found */ buffer++; n--; if( n > buflen ) goto too_short; if( ret_n ) *ret_n = n; offset = parse_one_sig_subpkt(buffer, n, type ); switch( offset ) { case -2: log_error("subpacket of type %d too short\n", type); return NULL; case -1: return NULL; default: break; } if( start ) *start = seq; return buffer+offset; } buffer += n; buflen -=n; } if( reqtype == SIGSUBPKT_TEST_CRITICAL ) return buffer; /* as value true to indicate that there is no */ /* critical bit we don't understand */ if( start ) *start = -1; return NULL; /* end of packets; not found */ too_short: if(opt.verbose) log_info("buffer shorter than subpacket\n"); if( start ) *start = -1; return NULL; } const byte * parse_sig_subpkt (const subpktarea_t *buffer, sigsubpkttype_t reqtype, size_t *ret_n) { return enum_sig_subpkt( buffer, reqtype, ret_n, NULL, NULL ); } const byte * parse_sig_subpkt2 (PKT_signature *sig, sigsubpkttype_t reqtype, size_t *ret_n ) { const byte *p; p = parse_sig_subpkt (sig->hashed, reqtype, ret_n ); if( !p ) p = parse_sig_subpkt (sig->unhashed, reqtype, ret_n ); return p; } /* Find all revocation keys. Look in hashed area only. */ void parse_revkeys(PKT_signature *sig) { struct revocation_key *revkey; int seq=0; size_t len; if(sig->sig_class!=0x1F) return; while((revkey= (struct revocation_key *)enum_sig_subpkt(sig->hashed, SIGSUBPKT_REV_KEY, &len,&seq,NULL))) { if(len==sizeof(struct revocation_key) && (revkey->class&0x80)) /* 0x80 bit must be set */ { sig->revkey=xrealloc(sig->revkey, sizeof(struct revocation_key *)*(sig->numrevkeys+1)); sig->revkey[sig->numrevkeys]=revkey; sig->numrevkeys++; } } } int parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, PKT_signature *sig ) { int md5_len=0; unsigned n; int is_v4=0; int rc=0; int i, ndata; if( pktlen < 16 ) { log_error("packet(%d) too short\n", pkttype); goto leave; } sig->version = iobuf_get_noeof(inp); pktlen--; if( sig->version == 4 ) is_v4=1; else if( sig->version != 2 && sig->version != 3 ) { log_error("packet(%d) with unknown version %d\n", pkttype, sig->version); rc = G10ERR_INVALID_PACKET; goto leave; } if( !is_v4 ) { md5_len = iobuf_get_noeof(inp); pktlen--; } sig->sig_class = iobuf_get_noeof(inp); pktlen--; if( !is_v4 ) { sig->timestamp = read_32(inp); pktlen -= 4; sig->keyid[0] = read_32(inp); pktlen -= 4; sig->keyid[1] = read_32(inp); pktlen -= 4; } sig->pubkey_algo = iobuf_get_noeof(inp); pktlen--; sig->digest_algo = iobuf_get_noeof(inp); pktlen--; sig->flags.exportable=1; sig->flags.revocable=1; if( is_v4 ) { /* read subpackets */ n = read_16(inp); pktlen -= 2; /* length of hashed data */ if( n > 10000 ) { log_error("signature packet: hashed data too long\n"); rc = G10ERR_INVALID_PACKET; goto leave; } if( n ) { sig->hashed = xmalloc (sizeof (*sig->hashed) + n - 1 ); sig->hashed->size = n; sig->hashed->len = n; if( iobuf_read (inp, sig->hashed->data, n ) != n ) { log_error ("premature eof while reading " "hashed signature data\n"); rc = -1; goto leave; } pktlen -= n; } n = read_16(inp); pktlen -= 2; /* length of unhashed data */ if( n > 10000 ) { log_error("signature packet: unhashed data too long\n"); rc = G10ERR_INVALID_PACKET; goto leave; } if( n ) { sig->unhashed = xmalloc (sizeof(*sig->unhashed) + n - 1 ); sig->unhashed->size = n; sig->unhashed->len = n; if( iobuf_read(inp, sig->unhashed->data, n ) != n ) { log_error("premature eof while reading " "unhashed signature data\n"); rc = -1; goto leave; } pktlen -= n; } } if( pktlen < 5 ) { /* sanity check */ log_error("packet(%d) too short\n", pkttype); rc = G10ERR_INVALID_PACKET; goto leave; } sig->digest_start[0] = iobuf_get_noeof(inp); pktlen--; sig->digest_start[1] = iobuf_get_noeof(inp); pktlen--; if( is_v4 && sig->pubkey_algo ) { /*extract required information */ const byte *p; size_t len; /* set sig->flags.unknown_critical if there is a * critical bit set for packets which we do not understand */ if( !parse_sig_subpkt (sig->hashed, SIGSUBPKT_TEST_CRITICAL, NULL) || !parse_sig_subpkt (sig->unhashed, SIGSUBPKT_TEST_CRITICAL, NULL) ) sig->flags.unknown_critical = 1; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_CREATED, NULL ); if(p) - sig->timestamp = buffer_to_u32(p); + sig->timestamp = buf32_to_u32 (p); else if(!(sig->pubkey_algo>=100 && sig->pubkey_algo<=110) && opt.verbose) log_info ("signature packet without timestamp\n"); p = parse_sig_subpkt2( sig, SIGSUBPKT_ISSUER, NULL ); if(p) { - sig->keyid[0] = buffer_to_u32(p); - sig->keyid[1] = buffer_to_u32(p+4); + sig->keyid[0] = buf32_to_u32 (p); + sig->keyid[1] = buf32_to_u32 (p+4); } else if(!(sig->pubkey_algo>=100 && sig->pubkey_algo<=110) && opt.verbose) log_info ("signature packet without keyid\n"); p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_SIG_EXPIRE,NULL); - if(p && buffer_to_u32(p)) - sig->expiredate=sig->timestamp+buffer_to_u32(p); + if(p && buf32_to_u32 (p)) + sig->expiredate = sig->timestamp + buf32_to_u32 (p); if(sig->expiredate && sig->expiredate<=make_timestamp()) sig->flags.expired=1; p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_POLICY,NULL); if(p) sig->flags.policy_url=1; p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_PREF_KS,NULL); if(p) sig->flags.pref_ks=1; p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,NULL); if(p) sig->flags.notation=1; p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_REVOCABLE,NULL); if(p && *p==0) sig->flags.revocable=0; p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_TRUST,&len); if(p && len==2) { sig->trust_depth=p[0]; sig->trust_value=p[1]; /* Only look for a regexp if there is also a trust subpacket. */ sig->trust_regexp= parse_sig_subpkt(sig->hashed,SIGSUBPKT_REGEXP,&len); /* If the regular expression is of 0 length, there is no regular expression. */ if(len==0) sig->trust_regexp=NULL; } /* We accept the exportable subpacket from either the hashed or unhashed areas as older versions of gpg put it in the unhashed area. In theory, anyway, we should never see this packet off of a local keyring. */ p=parse_sig_subpkt2(sig,SIGSUBPKT_EXPORTABLE,NULL); if(p && *p==0) sig->flags.exportable=0; /* Find all revocation keys. */ if(sig->sig_class==0x1F) parse_revkeys(sig); } if( list_mode ) { fprintf (listfp, ":signature packet: algo %d, keyid %08lX%08lX\n" "\tversion %d, created %lu, md5len %d, sigclass 0x%02x\n" "\tdigest algo %d, begin of digest %02x %02x\n", sig->pubkey_algo, (ulong)sig->keyid[0], (ulong)sig->keyid[1], sig->version, (ulong)sig->timestamp, md5_len, sig->sig_class, sig->digest_algo, sig->digest_start[0], sig->digest_start[1] ); if( is_v4 ) { parse_sig_subpkt (sig->hashed, SIGSUBPKT_LIST_HASHED, NULL ); parse_sig_subpkt (sig->unhashed, SIGSUBPKT_LIST_UNHASHED, NULL); } } ndata = pubkey_get_nsig(sig->pubkey_algo); if( !ndata ) { if( list_mode ) fprintf (listfp, "\tunknown algorithm %d\n", sig->pubkey_algo ); unknown_pubkey_warning( sig->pubkey_algo ); /* We store the plain material in data[0], so that we are able * to write it back with build_packet() */ if (pktlen > (5 * MAX_EXTERN_MPI_BITS/8)) { /* However we include a limit to avoid too trivial DoS attacks by having gpg allocate too much memory. */ log_error ("signature packet: too much data\n"); rc = G10ERR_INVALID_PACKET; } else { sig->data[0]= mpi_set_opaque (NULL, read_rest(inp, pktlen, 0), pktlen ); pktlen = 0; } } else { for( i=0; i < ndata; i++ ) { n = pktlen; sig->data[i] = mpi_read(inp, &n, 0 ); pktlen -=n; if( list_mode ) { fprintf (listfp, "\tdata: "); mpi_print(listfp, sig->data[i], mpi_print_mode ); putc ('\n', listfp); } if (!sig->data[i]) rc = G10ERR_INVALID_PACKET; } } leave: iobuf_skip_rest(inp, pktlen, 0); return rc; } static int parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen, PKT_onepass_sig *ops ) { int version; int rc = 0; if( pktlen < 13 ) { log_error("packet(%d) too short\n", pkttype); rc = G10ERR_INVALID_PACKET; goto leave; } version = iobuf_get_noeof(inp); pktlen--; if( version != 3 ) { log_error("onepass_sig with unknown version %d\n", version); rc = G10ERR_INVALID_PACKET; goto leave; } ops->sig_class = iobuf_get_noeof(inp); pktlen--; ops->digest_algo = iobuf_get_noeof(inp); pktlen--; ops->pubkey_algo = iobuf_get_noeof(inp); pktlen--; ops->keyid[0] = read_32(inp); pktlen -= 4; ops->keyid[1] = read_32(inp); pktlen -= 4; ops->last = iobuf_get_noeof(inp); pktlen--; if( list_mode ) fprintf (listfp, ":onepass_sig packet: keyid %08lX%08lX\n" "\tversion %d, sigclass 0x%02x, digest %d, pubkey %d, last=%d\n", (ulong)ops->keyid[0], (ulong)ops->keyid[1], version, ops->sig_class, ops->digest_algo, ops->pubkey_algo, ops->last ); leave: iobuf_skip_rest(inp, pktlen, 0); return rc; } static MPI read_protected_v3_mpi (IOBUF inp, unsigned long *length) { int c; unsigned int nbits, nbytes; unsigned char *buf, *p; MPI val; if (*length < 2) { log_error ("mpi too small\n"); return NULL; } if ((c=iobuf_get (inp)) == -1) return NULL; --*length; nbits = c << 8; if ((c=iobuf_get(inp)) == -1) return NULL; --*length; nbits |= c; if (nbits > 16384) { log_error ("mpi too large (%u bits)\n", nbits); return NULL; } nbytes = (nbits+7) / 8; buf = p = xmalloc (2 + nbytes); *p++ = nbits >> 8; *p++ = nbits; for (; nbytes && *length; nbytes--, --*length) *p++ = iobuf_get (inp); if (nbytes) { log_error ("packet shorter tham mpi\n"); xfree (buf); return NULL; } /* convert buffer into an opaque MPI */ val = mpi_set_opaque (NULL, buf, p-buf); return val; } static int parse_key( IOBUF inp, int pkttype, unsigned long pktlen, byte *hdr, int hdrlen, PACKET *pkt ) { int i, version, algorithm; unsigned n; unsigned long timestamp, expiredate, max_expiredate; int npkey, nskey; int is_v4=0; int rc=0; u32 keyid[2]; version = iobuf_get_noeof(inp); pktlen--; if( pkttype == PKT_PUBLIC_SUBKEY && version == '#' ) { /* early versions of G10 use old PGP comments packets; * luckily all those comments are started by a hash */ if( list_mode ) { fprintf (listfp, ":rfc1991 comment packet: \"" ); for( ; pktlen; pktlen-- ) { int c; c = iobuf_get_noeof(inp); if( c >= ' ' && c <= 'z' ) putc (c, listfp); else fprintf (listfp, "\\x%02x", c ); } fprintf (listfp, "\"\n"); } iobuf_skip_rest(inp, pktlen, 0); return 0; } else if( version == 4 ) is_v4=1; else if( version != 2 && version != 3 ) { log_error("packet(%d) with unknown version %d\n", pkttype, version); rc = G10ERR_INVALID_PACKET; goto leave; } if( pktlen < 11 ) { log_error("packet(%d) too short\n", pkttype); rc = G10ERR_INVALID_PACKET; goto leave; } else if (pktlen > MAX_KEY_PACKET_LENGTH) { log_error ("packet(%d) too large\n", pkttype); if (list_mode) fputs (":key packet: [too large]\n", listfp); rc = G10ERR_INVALID_PACKET; goto leave; } timestamp = read_32(inp); pktlen -= 4; if( is_v4 ) { expiredate = 0; /* have to get it from the selfsignature */ max_expiredate = 0; } else { unsigned short ndays; ndays = read_16(inp); pktlen -= 2; if( ndays ) expiredate = timestamp + ndays * 86400L; else expiredate = 0; max_expiredate=expiredate; } algorithm = iobuf_get_noeof(inp); pktlen--; if( list_mode ) fprintf (listfp, ":%s key packet:\n" "\tversion %d, algo %d, created %lu, expires %lu\n", pkttype == PKT_PUBLIC_KEY? "public" : pkttype == PKT_SECRET_KEY? "secret" : pkttype == PKT_PUBLIC_SUBKEY? "public sub" : pkttype == PKT_SECRET_SUBKEY? "secret sub" : "??", version, algorithm, timestamp, expiredate ); if( pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY ) { PKT_secret_key *sk = pkt->pkt.secret_key; sk->timestamp = timestamp; sk->expiredate = expiredate; sk->max_expiredate = max_expiredate; sk->hdrbytes = hdrlen; sk->version = version; sk->is_primary = pkttype == PKT_SECRET_KEY; sk->pubkey_algo = algorithm; sk->req_usage = 0; sk->pubkey_usage = 0; /* not yet used */ } else { PKT_public_key *pk = pkt->pkt.public_key; pk->timestamp = timestamp; pk->expiredate = expiredate; pk->max_expiredate = max_expiredate; pk->hdrbytes = hdrlen; pk->version = version; pk->is_primary = pkttype == PKT_PUBLIC_KEY; pk->pubkey_algo = algorithm; pk->req_usage = 0; pk->pubkey_usage = 0; /* not yet used */ pk->is_revoked = 0; pk->is_disabled = 0; pk->keyid[0] = 0; pk->keyid[1] = 0; } nskey = pubkey_get_nskey( algorithm ); npkey = pubkey_get_npkey( algorithm ); if( !npkey ) { if( list_mode ) fprintf (listfp, "\tunknown algorithm %d\n", algorithm ); unknown_pubkey_warning( algorithm ); } if( pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY ) { PKT_secret_key *sk = pkt->pkt.secret_key; byte temp[16]; size_t snlen = 0; if( !npkey ) { sk->skey[0] = mpi_set_opaque( NULL, read_rest(inp, pktlen, 0), pktlen ); pktlen = 0; goto leave; } for(i=0; i < npkey; i++ ) { n = pktlen; sk->skey[i] = mpi_read(inp, &n, 0 ); pktlen -=n; if( list_mode ) { fprintf (listfp, "\tskey[%d]: ", i); mpi_print(listfp, sk->skey[i], mpi_print_mode ); putc ('\n', listfp); } if (!sk->skey[i]) rc = G10ERR_INVALID_PACKET; } if (rc) /* one of the MPIs were bad */ goto leave; if (list_mode && npkey) keyid_from_sk (sk, keyid); sk->protect.algo = iobuf_get_noeof(inp); pktlen--; sk->protect.sha1chk = 0; if( sk->protect.algo ) { sk->is_protected = 1; sk->protect.s2k.count = 0; if( sk->protect.algo == 254 || sk->protect.algo == 255 ) { if( pktlen < 3 ) { rc = G10ERR_INVALID_PACKET; goto leave; } sk->protect.sha1chk = (sk->protect.algo == 254); sk->protect.algo = iobuf_get_noeof(inp); pktlen--; /* Note that a sk->protect.algo > 110 is illegal, but I'm not erroring on it here as otherwise there would be no way to delete such a key. */ sk->protect.s2k.mode = iobuf_get_noeof(inp); pktlen--; sk->protect.s2k.hash_algo = iobuf_get_noeof(inp); pktlen--; /* check for the special GNU extension */ if( is_v4 && sk->protect.s2k.mode == 101 ) { for(i=0; i < 4 && pktlen; i++, pktlen-- ) temp[i] = iobuf_get_noeof(inp); if( i < 4 || memcmp( temp, "GNU", 3 ) ) { if( list_mode ) fprintf (listfp, "\tunknown S2K %d\n", sk->protect.s2k.mode ); rc = G10ERR_INVALID_PACKET; goto leave; } /* here we know that it is a gnu extension * What follows is the GNU protection mode: * All values have special meanings * and they are mapped in the mode with a base of 1000. */ sk->protect.s2k.mode = 1000 + temp[3]; } switch( sk->protect.s2k.mode ) { case 1: case 3: for(i=0; i < 8 && pktlen; i++, pktlen-- ) temp[i] = iobuf_get_noeof(inp); memcpy(sk->protect.s2k.salt, temp, 8 ); break; } switch( sk->protect.s2k.mode ) { case 0: if( list_mode ) fprintf (listfp, "\tsimple S2K" ); break; case 1: if( list_mode ) fprintf (listfp, "\tsalted S2K" ); break; case 3: if( list_mode ) fprintf (listfp, "\titer+salt S2K" ); break; case 1001: if( list_mode ) fprintf (listfp, "\tgnu-dummy S2K" ); break; case 1002: if (list_mode) fprintf (listfp, "\tgnu-divert-to-card S2K"); break; default: if( list_mode ) fprintf (listfp, "\tunknown %sS2K %d\n", sk->protect.s2k.mode < 1000? "":"GNU ", sk->protect.s2k.mode ); rc = G10ERR_INVALID_PACKET; goto leave; } if( list_mode ) { fprintf (listfp, ", algo: %d,%s hash: %d", sk->protect.algo, sk->protect.sha1chk?" SHA1 protection," :" simple checksum,", sk->protect.s2k.hash_algo ); if( sk->protect.s2k.mode == 1 || sk->protect.s2k.mode == 3 ) { fprintf (listfp, ", salt: "); for(i=0; i < 8; i++ ) fprintf (listfp, "%02x", sk->protect.s2k.salt[i]); } putc ('\n', listfp); } if( sk->protect.s2k.mode == 3 ) { if( pktlen < 1 ) { rc = G10ERR_INVALID_PACKET; goto leave; } sk->protect.s2k.count = iobuf_get(inp); pktlen--; if( list_mode ) fprintf (listfp, "\tprotect count: %lu (%lu)\n", (ulong)S2K_DECODE_COUNT ((ulong)sk->protect.s2k.count), (ulong)sk->protect.s2k.count); } else if( sk->protect.s2k.mode == 1002 ) { /* Read the serial number. */ if (pktlen < 1) { rc = G10ERR_INVALID_PACKET; goto leave; } snlen = iobuf_get (inp); pktlen--; if (pktlen < snlen || snlen == (size_t)(-1)) { rc = G10ERR_INVALID_PACKET; goto leave; } } } /* Note that a sk->protect.algo > 110 is illegal, but I'm not erroring on it here as otherwise there would be no way to delete such a key. */ else { /* old version; no S2K, so we set mode to 0, hash MD5 */ sk->protect.s2k.mode = 0; sk->protect.s2k.hash_algo = DIGEST_ALGO_MD5; if( list_mode ) fprintf (listfp, "\tprotect algo: %d (hash algo: %d)\n", sk->protect.algo, sk->protect.s2k.hash_algo ); } /* It is really ugly that we don't know the size * of the IV here in cases we are not aware of the algorithm. * so a * sk->protect.ivlen = cipher_get_blocksize(sk->protect.algo); * won't work. The only solution I see is to hardwire it here. * NOTE: if you change the ivlen above 16, don't forget to * enlarge temp. */ switch( sk->protect.algo ) { case 7: case 8: case 9: /* AES */ case 10: /* Twofish */ case 11: case 12: case 13: /* Camellia */ sk->protect.ivlen = 16; break; default: sk->protect.ivlen = 8; } if( sk->protect.s2k.mode == 1001 ) sk->protect.ivlen = 0; else if( sk->protect.s2k.mode == 1002 ) sk->protect.ivlen = snlen < 16? snlen : 16; if( pktlen < sk->protect.ivlen ) { rc = G10ERR_INVALID_PACKET; goto leave; } for(i=0; i < sk->protect.ivlen && pktlen; i++, pktlen-- ) temp[i] = iobuf_get_noeof(inp); if( list_mode ) { fprintf (listfp, sk->protect.s2k.mode == 1002? "\tserial-number: " : "\tprotect IV: "); for(i=0; i < sk->protect.ivlen; i++ ) fprintf (listfp, " %02x", temp[i] ); putc ('\n', listfp); } memcpy(sk->protect.iv, temp, sk->protect.ivlen ); } else sk->is_protected = 0; /* It does not make sense to read it into secure memory. * If the user is so careless, not to protect his secret key, * we can assume, that he operates an open system :=(. * So we put the key into secure memory when we unprotect it. */ if( sk->protect.s2k.mode == 1001 || sk->protect.s2k.mode == 1002 ) { /* better set some dummy stuff here */ sk->skey[npkey] = mpi_set_opaque(NULL, xstrdup("dummydata"), 10); pktlen = 0; } else if( is_v4 && sk->is_protected ) { /* ugly; the length is encrypted too, so we read all * stuff up to the end of the packet into the first * skey element */ sk->skey[npkey] = mpi_set_opaque(NULL, read_rest(inp, pktlen, 0),pktlen); pktlen = 0; if( list_mode ) { fprintf (listfp, "\tencrypted stuff follows\n"); } } else { /* v3 method: the mpi length is not encrypted */ for(i=npkey; i < nskey; i++ ) { if ( sk->is_protected ) { sk->skey[i] = read_protected_v3_mpi (inp, &pktlen); if( list_mode ) fprintf (listfp, "\tskey[%d]: [encrypted]\n", i); } else { n = pktlen; sk->skey[i] = mpi_read(inp, &n, 0 ); pktlen -=n; if( list_mode ) { fprintf (listfp, "\tskey[%d]: ", i); mpi_print(listfp, sk->skey[i], mpi_print_mode ); putc ('\n', listfp); } } if (!sk->skey[i]) rc = G10ERR_INVALID_PACKET; } if (rc) goto leave; sk->csum = read_16(inp); pktlen -= 2; if( list_mode ) { fprintf (listfp, "\tchecksum: %04hx\n", sk->csum); } } } else { PKT_public_key *pk = pkt->pkt.public_key; if( !npkey ) { pk->pkey[0] = mpi_set_opaque( NULL, read_rest(inp, pktlen, 0), pktlen ); pktlen = 0; goto leave; } for(i=0; i < npkey; i++ ) { n = pktlen; pk->pkey[i] = mpi_read(inp, &n, 0 ); pktlen -=n; if( list_mode ) { fprintf (listfp, "\tpkey[%d]: ", i); mpi_print(listfp, pk->pkey[i], mpi_print_mode ); putc ('\n', listfp); } if (!pk->pkey[i]) rc = G10ERR_INVALID_PACKET; } if (rc) goto leave; if (list_mode) keyid_from_pk (pk, keyid); } if (list_mode && npkey) fprintf (listfp, "\tkeyid: %08lX%08lX\n", (ulong) keyid[0], (ulong) keyid[1]); leave: iobuf_skip_rest(inp, pktlen, 0); return rc; } /* Attribute subpackets have the same format as v4 signature subpackets. This is not part of OpenPGP, but is done in several versions of PGP nevertheless. */ int parse_attribute_subpkts(PKT_user_id *uid) { size_t n; int count=0; struct user_attribute *attribs=NULL; const byte *buffer=uid->attrib_data; int buflen=uid->attrib_len; byte type; xfree(uid->attribs); while(buflen) { n = *buffer++; buflen--; if( n == 255 ) { /* 4 byte length header */ if( buflen < 4 ) goto too_short; - n = (buffer[0] << 24) | (buffer[1] << 16) - | (buffer[2] << 8) | buffer[3]; - buffer += 4; + n = buf32_to_size_t (buffer); + buffer += 4; buflen -= 4; } else if( n >= 192 ) { /* 2 byte special encoded length header */ if( buflen < 2 ) goto too_short; n = (( n - 192 ) << 8) + *buffer + 192; buffer++; buflen--; } if( buflen < n ) goto too_short; if (!n) { /* Too short to encode the subpacket type. */ if (opt.verbose) log_info ("attribute subpacket too short\n"); break; } attribs=xrealloc(attribs,(count+1)*sizeof(struct user_attribute)); memset(&attribs[count],0,sizeof(struct user_attribute)); type=*buffer; buffer++; buflen--; n--; attribs[count].type=type; attribs[count].data=buffer; attribs[count].len=n; buffer+=n; buflen-=n; count++; } uid->attribs=attribs; uid->numattribs=count; return count; too_short: if(opt.verbose) log_info("buffer shorter than attribute subpacket\n"); uid->attribs=attribs; uid->numattribs=count; return count; } static int parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) { byte *p; /* Cap the size of a user ID at 2k: a value absurdly large enough that there is no sane user ID string (which is printable text as of RFC2440bis) that won't fit in it, but yet small enough to avoid allocation problems. A large pktlen may not be allocatable, and a very large pktlen could actually cause our allocation to wrap around in xmalloc to a small number. */ if (pktlen > MAX_UID_PACKET_LENGTH) { log_error("packet(%d) too large\n", pkttype); iobuf_skip_rest(inp, pktlen, 0); return G10ERR_INVALID_PACKET; } packet->pkt.user_id = xmalloc_clear(sizeof *packet->pkt.user_id + pktlen); packet->pkt.user_id->len = pktlen; packet->pkt.user_id->ref=1; p = packet->pkt.user_id->name; for( ; pktlen; pktlen--, p++ ) *p = iobuf_get_noeof(inp); *p = 0; if( list_mode ) { int n = packet->pkt.user_id->len; fprintf (listfp, ":user ID packet: \""); /* fixme: Hey why don't we replace this with print_string?? */ for(p=packet->pkt.user_id->name; n; p++, n-- ) { if( *p >= ' ' && *p <= 'z' ) putc (*p, listfp); else fprintf (listfp, "\\x%02x", *p ); } fprintf (listfp, "\"\n"); } return 0; } void make_attribute_uidname(PKT_user_id *uid, size_t max_namelen) { assert ( max_namelen > 70 ); if(uid->numattribs<=0) sprintf(uid->name,"[bad attribute packet of size %lu]",uid->attrib_len); else if(uid->numattribs>1) sprintf(uid->name,"[%d attributes of size %lu]", uid->numattribs,uid->attrib_len); else { /* Only one attribute, so list it as the "user id" */ if(uid->attribs->type==ATTRIB_IMAGE) { u32 len; byte type; if(parse_image_header(uid->attribs,&type,&len)) sprintf(uid->name,"[%.20s image of size %lu]", image_type_to_string(type,1),(ulong)len); else sprintf(uid->name,"[invalid image]"); } else sprintf(uid->name,"[unknown attribute of size %lu]", (ulong)uid->attribs->len); } uid->len = strlen(uid->name); } static int parse_attribute( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) { byte *p; (void)pkttype; /* We better cap the size of an attribute packet to make DoS not too easy. 16MB should be more then enough for one attribute packet (ie. a photo). */ if (pktlen > MAX_ATTR_PACKET_LENGTH) { log_error ("packet(%d) too large\n", pkttype); if (list_mode) fprintf (listfp, ":attribute packet: [too large]\n"); iobuf_skip_rest (inp, pktlen, 0); return G10ERR_INVALID_PACKET; } #define EXTRA_UID_NAME_SPACE 71 packet->pkt.user_id = xmalloc_clear(sizeof *packet->pkt.user_id + EXTRA_UID_NAME_SPACE); packet->pkt.user_id->ref=1; packet->pkt.user_id->attrib_data = xmalloc(pktlen); packet->pkt.user_id->attrib_len = pktlen; p = packet->pkt.user_id->attrib_data; for( ; pktlen; pktlen--, p++ ) *p = iobuf_get_noeof(inp); /* Now parse out the individual attribute subpackets. This is somewhat pointless since there is only one currently defined attribute type (jpeg), but it is correct by the spec. */ parse_attribute_subpkts(packet->pkt.user_id); make_attribute_uidname(packet->pkt.user_id, EXTRA_UID_NAME_SPACE); if( list_mode ) { fprintf (listfp, ":attribute packet: %s\n", packet->pkt.user_id->name ); } return 0; } static int parse_comment( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) { byte *p; /* Cap comment packet at a reasonable value to avoid an integer overflow in the malloc below. Comment packets are actually not anymore define my OpenPGP and we even stopped to use our private comment packet. */ if (pktlen > MAX_COMMENT_PACKET_LENGTH) { log_error ("packet(%d) too large\n", pkttype); iobuf_skip_rest (inp, pktlen, 0); return G10ERR_INVALID_PACKET; } packet->pkt.comment = xmalloc(sizeof *packet->pkt.comment + pktlen - 1); packet->pkt.comment->len = pktlen; p = packet->pkt.comment->data; for( ; pktlen; pktlen--, p++ ) *p = iobuf_get_noeof(inp); if( list_mode ) { int n = packet->pkt.comment->len; fprintf (listfp, ":%scomment packet: \"", pkttype == PKT_OLD_COMMENT? "OpenPGP draft " : "GnuPG " ); for(p=packet->pkt.comment->data; n; p++, n-- ) { if( *p >= ' ' && *p <= 'z' ) putc (*p, listfp); else fprintf (listfp, "\\x%02x", *p ); } fprintf (listfp, "\"\n"); } return 0; } static void parse_trust( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt ) { int c; (void)pkttype; pkt->pkt.ring_trust = xmalloc( sizeof *pkt->pkt.ring_trust ); if (pktlen) { c = iobuf_get_noeof(inp); pktlen--; pkt->pkt.ring_trust->trustval = c; pkt->pkt.ring_trust->sigcache = 0; if (!c && pktlen==1) { c = iobuf_get_noeof (inp); pktlen--; /* we require that bit 7 of the sigcache is 0 (easier eof handling)*/ if ( !(c & 0x80) ) pkt->pkt.ring_trust->sigcache = c; } if( list_mode ) fprintf (listfp, ":trust packet: flag=%02x sigcache=%02x\n", pkt->pkt.ring_trust->trustval, pkt->pkt.ring_trust->sigcache); } else { pkt->pkt.ring_trust->trustval = 0; pkt->pkt.ring_trust->sigcache = 0; if (list_mode) fprintf (listfp, ":trust packet: empty\n"); } iobuf_skip_rest (inp, pktlen, 0); } static int parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt, int new_ctb, int partial ) { int rc = 0; int mode, namelen; PKT_plaintext *pt; byte *p; int c, i; if( !partial && pktlen < 6 ) { log_error("packet(%d) too short (%lu)\n", pkttype, (ulong)pktlen); rc = G10ERR_INVALID_PACKET; goto leave; } mode = iobuf_get_noeof(inp); if( pktlen ) pktlen--; namelen = iobuf_get_noeof(inp); if( pktlen ) pktlen--; /* Note that namelen will never exceeds 255 byte. */ pt = pkt->pkt.plaintext = xmalloc(sizeof *pkt->pkt.plaintext + namelen -1); pt->new_ctb = new_ctb; pt->mode = mode; pt->namelen = namelen; pt->is_partial = partial; if( pktlen ) { for( i=0; pktlen > 4 && i < namelen; pktlen--, i++ ) pt->name[i] = iobuf_get_noeof(inp); } else { for( i=0; i < namelen; i++ ) if( (c=iobuf_get(inp)) == -1 ) break; else pt->name[i] = c; } pt->timestamp = read_32(inp); if( pktlen) pktlen -= 4; pt->len = pktlen; pt->buf = inp; pktlen = 0; if( list_mode ) { fprintf (listfp, ":literal data packet:\n" "\tmode %c (%X), created %lu, name=\"", mode >= ' ' && mode <'z'? mode : '?', mode, (ulong)pt->timestamp ); for(p=pt->name,i=0; i < namelen; p++, i++ ) { if( *p >= ' ' && *p <= 'z' ) putc (*p, listfp); else fprintf (listfp, "\\x%02x", *p ); } fprintf (listfp, "\",\n\traw data: "); if(partial) fprintf (listfp, "unknown length\n"); else fprintf (listfp, "%lu bytes\n", (ulong)pt->len ); } leave: return rc; } static int parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt, int new_ctb ) { PKT_compressed *zd; /* pktlen is here 0, but data follows * (this should be the last object in a file or * the compress algorithm should know the length) */ zd = pkt->pkt.compressed = xmalloc(sizeof *pkt->pkt.compressed ); zd->algorithm = iobuf_get_noeof(inp); zd->len = 0; /* not used */ zd->new_ctb = new_ctb; zd->buf = inp; if( list_mode ) fprintf (listfp, ":compressed packet: algo=%d\n", zd->algorithm); return 0; } static int parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt, int new_ctb, int partial ) { int rc = 0; PKT_encrypted *ed; unsigned long orig_pktlen = pktlen; ed = pkt->pkt.encrypted = xmalloc(sizeof *pkt->pkt.encrypted ); ed->len = pktlen; /* we don't know the extralen which is (cipher_blocksize+2) because the algorithm ist not specified in this packet. However, it is only important to know this for some sanity checks on the packet length - it doesn't matter that we can't do it */ ed->extralen = 0; ed->buf = NULL; ed->new_ctb = new_ctb; ed->is_partial = partial; ed->mdc_method = 0; if( pkttype == PKT_ENCRYPTED_MDC ) { /* fixme: add some pktlen sanity checks */ int version; version = iobuf_get_noeof(inp); if (orig_pktlen) pktlen--; if( version != 1 ) { log_error("encrypted_mdc packet with unknown version %d\n", version); /*skip_rest(inp, pktlen); should we really do this? */ rc = G10ERR_INVALID_PACKET; goto leave; } ed->mdc_method = DIGEST_ALGO_SHA1; } if( orig_pktlen && pktlen < 10 ) { /* actually this is blocksize+2 */ log_error("packet(%d) too short\n", pkttype); rc = G10ERR_INVALID_PACKET; iobuf_skip_rest(inp, pktlen, partial); goto leave; } if( list_mode ) { if( orig_pktlen ) fprintf (listfp, ":encrypted data packet:\n\tlength: %lu\n", orig_pktlen); else fprintf (listfp, ":encrypted data packet:\n\tlength: unknown\n"); if( ed->mdc_method ) fprintf (listfp, "\tmdc_method: %d\n", ed->mdc_method ); } ed->buf = inp; leave: return rc; } static int parse_mdc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt, int new_ctb ) { int rc = 0; PKT_mdc *mdc; byte *p; mdc = pkt->pkt.mdc= xmalloc(sizeof *pkt->pkt.mdc ); if( list_mode ) fprintf (listfp, ":mdc packet: length=%lu\n", pktlen); if( !new_ctb || pktlen != 20 ) { log_error("mdc_packet with invalid encoding\n"); rc = G10ERR_INVALID_PACKET; goto leave; } p = mdc->hash; for( ; pktlen; pktlen--, p++ ) *p = iobuf_get_noeof(inp); leave: return rc; } /* * This packet is internally generated by GPG (by armor.c) to * transfer some information to the lower layer. To make sure that * this packet is really a GPG faked one and not one comming from outside, * we first check that there is a unique tag in it. * The format of such a control packet is: * n byte session marker * 1 byte control type CTRLPKT_xxxxx * m byte control data */ static int parse_gpg_control( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet, int partial ) { byte *p; const byte *sesmark; size_t sesmarklen; int i; if ( list_mode ) fprintf (listfp, ":packet 63: length %lu ", pktlen); sesmark = get_session_marker ( &sesmarklen ); if ( pktlen < sesmarklen+1 ) /* 1 is for the control bytes */ goto skipit; for( i=0; i < sesmarklen; i++, pktlen-- ) { if ( sesmark[i] != iobuf_get_noeof(inp) ) goto skipit; } if (pktlen > 4096) goto skipit; /* Definitely too large. We skip it to avoid an overflow in the malloc. */ if ( list_mode ) puts ("- gpg control packet"); packet->pkt.gpg_control = xmalloc(sizeof *packet->pkt.gpg_control + pktlen - 1); packet->pkt.gpg_control->control = iobuf_get_noeof(inp); pktlen--; packet->pkt.gpg_control->datalen = pktlen; p = packet->pkt.gpg_control->data; for( ; pktlen; pktlen--, p++ ) *p = iobuf_get_noeof(inp); return 0; skipit: if ( list_mode ) { int c; i=0; fprintf (listfp, "- private (rest length %lu)\n", pktlen); if( partial ) { while( (c=iobuf_get(inp)) != -1 ) dump_hex_line(c, &i); } else { for( ; pktlen; pktlen-- ) { dump_hex_line ((c=iobuf_get (inp)), &i); if (c == -1) break; } } putc ('\n', listfp); } iobuf_skip_rest(inp,pktlen, 0); return G10ERR_INVALID_PACKET; } /* create a gpg control packet to be used internally as a placeholder */ PACKET * create_gpg_control( ctrlpkttype_t type, const byte *data, size_t datalen ) { PACKET *packet; byte *p; packet = xmalloc( sizeof *packet ); init_packet(packet); packet->pkttype = PKT_GPG_CONTROL; packet->pkt.gpg_control = xmalloc(sizeof *packet->pkt.gpg_control + datalen - 1); packet->pkt.gpg_control->control = type; packet->pkt.gpg_control->datalen = datalen; p = packet->pkt.gpg_control->data; for( ; datalen; datalen--, p++ ) *p = *data++; return packet; } diff --git a/g10/tdbio.c b/g10/tdbio.c index f109dde02..403b6087f 100644 --- a/g10/tdbio.c +++ b/g10/tdbio.c @@ -1,1643 +1,1643 @@ /* tdbio.c * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2012 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 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include #include #include "errors.h" #include "iobuf.h" #include "memory.h" #include "util.h" #include "options.h" #include "main.h" #include "i18n.h" #include "trustdb.h" #include "tdbio.h" #if defined(HAVE_DOSISH_SYSTEM) #define ftruncate chsize #endif #if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__) #define MY_O_BINARY O_BINARY #else #define MY_O_BINARY 0 #endif /**************** * Yes, this is a very simple implementation. We should really * use a page aligned buffer and read complete pages. * To implement a simple trannsaction system, this is sufficient. */ typedef struct cache_ctrl_struct *CACHE_CTRL; struct cache_ctrl_struct { CACHE_CTRL next; struct { unsigned used:1; unsigned dirty:1; } flags; ulong recno; char data[TRUST_RECORD_LEN]; }; #define MAX_CACHE_ENTRIES_SOFT 200 /* may be increased while in a */ #define MAX_CACHE_ENTRIES_HARD 10000 /* transaction to this one */ static CACHE_CTRL cache_list; static int cache_entries; static int cache_is_dirty; /* a type used to pass infomation to cmp_krec_fpr */ struct cmp_krec_fpr_struct { int pubkey_algo; const char *fpr; int fprlen; }; /* a type used to pass infomation to cmp_[s]dir */ struct cmp_xdir_struct { int pubkey_algo; u32 keyid[2]; }; static char *db_name; static dotlock_t lockhandle; static int is_locked; static int db_fd = -1; static int in_transaction; static void open_db(void); static void migrate_from_v2 (void); /************************************* ************* record cache ********** *************************************/ /**************** * Get the data from therecord cache and return a * pointer into that cache. Caller should copy * the return data. NULL is returned on a cache miss. */ static const char * get_record_from_cache( ulong recno ) { CACHE_CTRL r; for( r = cache_list; r; r = r->next ) { if( r->flags.used && r->recno == recno ) return r->data; } return NULL; } static int write_cache_item( CACHE_CTRL r ) { int n; if( lseek( db_fd, r->recno * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) { log_error(_("trustdb rec %lu: lseek failed: %s\n"), r->recno, strerror(errno) ); return G10ERR_WRITE_FILE; } n = write( db_fd, r->data, TRUST_RECORD_LEN); if( n != TRUST_RECORD_LEN ) { log_error(_("trustdb rec %lu: write failed (n=%d): %s\n"), r->recno, n, strerror(errno) ); return G10ERR_WRITE_FILE; } r->flags.dirty = 0; return 0; } /**************** * Put data into the cache. This function may flush the * some cache entries if there is not enough space available. */ int put_record_into_cache( ulong recno, const char *data ) { CACHE_CTRL r, unused; int dirty_count = 0; int clean_count = 0; /* see whether we already cached this one */ for( unused = NULL, r = cache_list; r; r = r->next ) { if( !r->flags.used ) { if( !unused ) unused = r; } else if( r->recno == recno ) { if( !r->flags.dirty ) { /* Hmmm: should we use a a copy and compare? */ if( memcmp(r->data, data, TRUST_RECORD_LEN ) ) { r->flags.dirty = 1; cache_is_dirty = 1; } } memcpy( r->data, data, TRUST_RECORD_LEN ); return 0; } if( r->flags.used ) { if( r->flags.dirty ) dirty_count++; else clean_count++; } } /* not in the cache: add a new entry */ if( unused ) { /* reuse this entry */ r = unused; r->flags.used = 1; r->recno = recno; memcpy( r->data, data, TRUST_RECORD_LEN ); r->flags.dirty = 1; cache_is_dirty = 1; cache_entries++; return 0; } /* see whether we reached the limit */ if( cache_entries < MAX_CACHE_ENTRIES_SOFT ) { /* no */ r = xmalloc( sizeof *r ); r->flags.used = 1; r->recno = recno; memcpy( r->data, data, TRUST_RECORD_LEN ); r->flags.dirty = 1; r->next = cache_list; cache_list = r; cache_is_dirty = 1; cache_entries++; return 0; } /* cache is full: discard some clean entries */ if( clean_count ) { int n = clean_count / 3; /* discard a third of the clean entries */ if( !n ) n = 1; for( unused = NULL, r = cache_list; r; r = r->next ) { if( r->flags.used && !r->flags.dirty ) { if( !unused ) unused = r; r->flags.used = 0; cache_entries--; if( !--n ) break; } } assert( unused ); r = unused; r->flags.used = 1; r->recno = recno; memcpy( r->data, data, TRUST_RECORD_LEN ); r->flags.dirty = 1; cache_is_dirty = 1; cache_entries++; return 0; } /* no clean entries: have to flush some dirty entries */ if( in_transaction ) { /* but we can't do this while in a transaction * we increase the cache size instead */ if( cache_entries < MAX_CACHE_ENTRIES_HARD ) { /* no */ if( opt.debug && !(cache_entries % 100) ) log_debug("increasing tdbio cache size\n"); r = xmalloc( sizeof *r ); r->flags.used = 1; r->recno = recno; memcpy( r->data, data, TRUST_RECORD_LEN ); r->flags.dirty = 1; r->next = cache_list; cache_list = r; cache_is_dirty = 1; cache_entries++; return 0; } log_info(_("trustdb transaction too large\n")); return G10ERR_RESOURCE_LIMIT; } if( dirty_count ) { int n = dirty_count / 5; /* discard some dirty entries */ if( !n ) n = 1; if( !is_locked ) { if (dotlock_take (lockhandle, -1)) log_fatal("can't acquire lock - giving up\n"); else is_locked = 1; } for( unused = NULL, r = cache_list; r; r = r->next ) { if( r->flags.used && r->flags.dirty ) { int rc = write_cache_item( r ); if( rc ) return rc; if( !unused ) unused = r; r->flags.used = 0; cache_entries--; if( !--n ) break; } } if( !opt.lock_once ) { if (!dotlock_release (lockhandle)) is_locked = 0; } assert( unused ); r = unused; r->flags.used = 1; r->recno = recno; memcpy( r->data, data, TRUST_RECORD_LEN ); r->flags.dirty = 1; cache_is_dirty = 1; cache_entries++; return 0; } BUG(); } int tdbio_is_dirty() { return cache_is_dirty; } /**************** * Flush the cache. This cannot be used while in a transaction. */ int tdbio_sync() { CACHE_CTRL r; int did_lock = 0; if( db_fd == -1 ) open_db(); if( in_transaction ) log_bug("tdbio: syncing while in transaction\n"); if( !cache_is_dirty ) return 0; if( !is_locked ) { if (dotlock_take (lockhandle, -1)) log_fatal("can't acquire lock - giving up\n"); else is_locked = 1; did_lock = 1; } for( r = cache_list; r; r = r->next ) { if( r->flags.used && r->flags.dirty ) { int rc = write_cache_item( r ); if( rc ) return rc; } } cache_is_dirty = 0; if( did_lock && !opt.lock_once ) { if (!dotlock_release (lockhandle)) is_locked = 0; } return 0; } #if 0 /* The transaction code is disabled in the 1.2.x branch, as it is not yet used. It will be enabled in 1.3.x. */ /**************** * Simple transactions system: * Everything between begin_transaction and end/cancel_transaction * is not immediatly written but at the time of end_transaction. * */ int tdbio_begin_transaction() { int rc; if( in_transaction ) log_bug("tdbio: nested transactions\n"); /* flush everything out */ rc = tdbio_sync(); if( rc ) return rc; in_transaction = 1; return 0; } int tdbio_end_transaction() { int rc; if( !in_transaction ) log_bug("tdbio: no active transaction\n"); if( !is_locked ) { if (dotlock_take (lockhandle, -1)) log_fatal("can't acquire lock - giving up\n"); else is_locked = 1; } block_all_signals(); in_transaction = 0; rc = tdbio_sync(); unblock_all_signals(); if( !opt.lock_once ) { if (!dotlock_release (lockhandle)) is_locked = 0; } return rc; } int tdbio_cancel_transaction() { CACHE_CTRL r; if( !in_transaction ) log_bug("tdbio: no active transaction\n"); /* remove all dirty marked entries, so that the original ones * are read back the next time */ if( cache_is_dirty ) { for( r = cache_list; r; r = r->next ) { if( r->flags.used && r->flags.dirty ) { r->flags.used = 0; cache_entries--; } } cache_is_dirty = 0; } in_transaction = 0; return 0; } #endif /******************************************************** **************** cached I/O functions ****************** ********************************************************/ static void cleanup(void) { if( is_locked ) { if (!dotlock_release (lockhandle)) is_locked = 0; } } /* Caller must sync */ int tdbio_update_version_record (void) { TRUSTREC rec; int rc; memset( &rec, 0, sizeof rec ); rc=tdbio_read_record( 0, &rec, RECTYPE_VER); if(rc==0) { rec.r.ver.created = make_timestamp(); rec.r.ver.marginals = opt.marginals_needed; rec.r.ver.completes = opt.completes_needed; rec.r.ver.cert_depth = opt.max_cert_depth; rec.r.ver.trust_model = opt.trust_model; rec.r.ver.min_cert_level = opt.min_cert_level; rc=tdbio_write_record(&rec); } return rc; } static int create_version_record (void) { TRUSTREC rec; int rc; memset( &rec, 0, sizeof rec ); rec.r.ver.version = 3; rec.r.ver.created = make_timestamp(); rec.r.ver.marginals = opt.marginals_needed; rec.r.ver.completes = opt.completes_needed; rec.r.ver.cert_depth = opt.max_cert_depth; if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC) rec.r.ver.trust_model = opt.trust_model; else rec.r.ver.trust_model = TM_PGP; rec.r.ver.min_cert_level = opt.min_cert_level; rec.rectype = RECTYPE_VER; rec.recnum = 0; rc = tdbio_write_record( &rec ); if( !rc ) tdbio_sync(); return rc; } int tdbio_set_dbname( const char *new_dbname, int create, int *r_nofile) { char *fname; static int initialized = 0; if( !initialized ) { atexit( cleanup ); initialized = 1; } *r_nofile = 0; if(new_dbname==NULL) fname=make_filename(opt.homedir,"trustdb" EXTSEP_S "gpg", NULL); else if (*new_dbname != DIRSEP_C ) { if (strchr(new_dbname, DIRSEP_C) ) fname = make_filename (new_dbname, NULL); else fname = make_filename (opt.homedir, new_dbname, NULL); } else fname = xstrdup (new_dbname); if( access( fname, R_OK ) ) { if( errno != ENOENT ) { log_error( _("can't access `%s': %s\n"), fname, strerror(errno) ); xfree(fname); return G10ERR_TRUSTDB; } if (!create) *r_nofile = 1; else { FILE *fp; TRUSTREC rec; int rc; char *p = strrchr( fname, DIRSEP_C ); mode_t oldmask; assert(p); *p = 0; if( access( fname, F_OK ) ) { try_make_homedir( fname ); if (access (fname, F_OK )) log_fatal (_("%s: directory does not exist!\n"), fname); } *p = DIRSEP_C; xfree(db_name); db_name = fname; #ifdef __riscos__ if( !lockhandle ) lockhandle = dotlock_create (db_name, 0); if( !lockhandle ) log_fatal( _("can't create lock for `%s'\n"), db_name ); if (dotlock_take (lockhandle, -1)) log_fatal( _("can't lock `%s'\n"), db_name ); #endif /* __riscos__ */ oldmask=umask(077); if (is_secured_filename (fname)) { fp = NULL; errno = EPERM; } else fp =fopen( fname, "wb" ); umask(oldmask); if( !fp ) log_fatal( _("can't create `%s': %s\n"), fname, strerror(errno) ); fclose(fp); db_fd = open( db_name, O_RDWR | MY_O_BINARY ); if( db_fd == -1 ) log_fatal( _("can't open `%s': %s\n"), db_name, strerror(errno) ); #ifndef __riscos__ if( !lockhandle ) lockhandle = dotlock_create (db_name, 0); if( !lockhandle ) log_fatal( _("can't create lock for `%s'\n"), db_name ); #endif /* !__riscos__ */ rc = create_version_record (); if( rc ) log_fatal( _("%s: failed to create version record: %s"), fname, g10_errstr(rc)); /* and read again to check that we are okay */ if( tdbio_read_record( 0, &rec, RECTYPE_VER ) ) log_fatal( _("%s: invalid trustdb created\n"), db_name ); if( !opt.quiet ) log_info(_("%s: trustdb created\n"), db_name); return 0; } } xfree(db_name); db_name = fname; return 0; } const char * tdbio_get_dbname() { return db_name; } static void open_db() { byte buf[10]; int n; TRUSTREC rec; assert( db_fd == -1 ); if (!lockhandle ) lockhandle = dotlock_create (db_name, 0); if (!lockhandle ) log_fatal( _("can't create lock for `%s'\n"), db_name ); #ifdef __riscos__ if (dotlock_take (lockhandle, -1)) log_fatal( _("can't lock `%s'\n"), db_name ); #endif /* __riscos__ */ db_fd = open (db_name, O_RDWR | MY_O_BINARY ); if (db_fd == -1 && (errno == EACCES #ifdef EROFS || errno == EROFS #endif ) ) { db_fd = open (db_name, O_RDONLY | MY_O_BINARY ); if (db_fd != -1) log_info (_("NOTE: trustdb not writable\n")); } if ( db_fd == -1 ) log_fatal( _("can't open `%s': %s\n"), db_name, strerror(errno) ); register_secured_file (db_name); /* check whether we need to do a version migration */ do n = read (db_fd, buf, 5); while (n==-1 && errno == EINTR); if (n == 5 && !memcmp (buf, "\x01gpg\x02", 5)) { migrate_from_v2 (); } /* read the version record */ if (tdbio_read_record (0, &rec, RECTYPE_VER ) ) log_fatal( _("%s: invalid trustdb\n"), db_name ); } /**************** * Make a hashtable: type 0 = trust hash */ static void create_hashtable( TRUSTREC *vr, int type ) { TRUSTREC rec; off_t offset; ulong recnum; int i, n, rc; offset = lseek( db_fd, 0, SEEK_END ); if( offset == -1 ) log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) ); recnum = offset / TRUST_RECORD_LEN; assert(recnum); /* this is will never be the first record */ if( !type ) vr->r.ver.trusthashtbl = recnum; /* Now write the records */ n = (256+ITEMS_PER_HTBL_RECORD-1) / ITEMS_PER_HTBL_RECORD; for(i=0; i < n; i++, recnum++ ) { memset( &rec, 0, sizeof rec ); rec.rectype = RECTYPE_HTBL; rec.recnum = recnum; rc = tdbio_write_record( &rec ); if( rc ) log_fatal( _("%s: failed to create hashtable: %s\n"), db_name, g10_errstr(rc)); } /* update the version record */ rc = tdbio_write_record( vr ); if( !rc ) rc = tdbio_sync(); if( rc ) log_fatal( _("%s: error updating version record: %s\n"), db_name, g10_errstr(rc)); } int tdbio_db_matches_options() { static int yes_no = -1; if( yes_no == -1 ) { TRUSTREC vr; int rc; rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, g10_errstr(rc) ); yes_no = vr.r.ver.marginals == opt.marginals_needed && vr.r.ver.completes == opt.completes_needed && vr.r.ver.cert_depth == opt.max_cert_depth && vr.r.ver.trust_model == opt.trust_model && vr.r.ver.min_cert_level == opt.min_cert_level; } return yes_no; } byte tdbio_read_model(void) { TRUSTREC vr; int rc; rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, g10_errstr(rc) ); return vr.r.ver.trust_model; } /**************** * Return the nextstamp value. */ ulong tdbio_read_nextcheck () { TRUSTREC vr; int rc; rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, g10_errstr(rc) ); return vr.r.ver.nextcheck; } /* Return true when the stamp was actually changed. */ int tdbio_write_nextcheck (ulong stamp) { TRUSTREC vr; int rc; rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, g10_errstr(rc) ); if (vr.r.ver.nextcheck == stamp) return 0; vr.r.ver.nextcheck = stamp; rc = tdbio_write_record( &vr ); if( rc ) log_fatal( _("%s: error writing version record: %s\n"), db_name, g10_errstr(rc) ); return 1; } /**************** * Return the record number of the trusthash tbl or create a new one. */ static ulong get_trusthashrec(void) { static ulong trusthashtbl; /* record number of the trust hashtable */ if( !trusthashtbl ) { TRUSTREC vr; int rc; rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, g10_errstr(rc) ); if( !vr.r.ver.trusthashtbl ) create_hashtable( &vr, 0 ); trusthashtbl = vr.r.ver.trusthashtbl; } return trusthashtbl; } /**************** * Update a hashtable. * table gives the start of the table, key and keylen is the key, * newrecnum is the record number to insert. */ static int upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum ) { TRUSTREC lastrec, rec; ulong hashrec, item; int msb; int level=0; int rc, i; hashrec = table; next_level: msb = key[level]; hashrec += msb / ITEMS_PER_HTBL_RECORD; rc = tdbio_read_record( hashrec, &rec, RECTYPE_HTBL ); if( rc ) { log_error("upd_hashtable: read failed: %s\n", g10_errstr(rc) ); return rc; } item = rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD]; if( !item ) { /* insert a new item into the hash table */ rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = newrecnum; rc = tdbio_write_record( &rec ); if( rc ) { log_error("upd_hashtable: write htbl failed: %s\n", g10_errstr(rc) ); return rc; } } else if( item != newrecnum ) { /* must do an update */ lastrec = rec; rc = tdbio_read_record( item, &rec, 0 ); if( rc ) { log_error( "upd_hashtable: read item failed: %s\n", g10_errstr(rc) ); return rc; } if( rec.rectype == RECTYPE_HTBL ) { hashrec = item; level++; if( level >= keylen ) { log_error( "hashtable has invalid indirections.\n"); return G10ERR_TRUSTDB; } goto next_level; } else if( rec.rectype == RECTYPE_HLST ) { /* extend list */ /* see whether the key is already in this list */ for(;;) { for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { if( rec.r.hlst.rnum[i] == newrecnum ) { return 0; /* okay, already in the list */ } } if( rec.r.hlst.next ) { rc = tdbio_read_record( rec.r.hlst.next, &rec, RECTYPE_HLST); if( rc ) { log_error( "upd_hashtable: read hlst failed: %s\n", g10_errstr(rc) ); return rc; } } else break; /* not there */ } /* find the next free entry and put it in */ for(;;) { for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { if( !rec.r.hlst.rnum[i] ) { rec.r.hlst.rnum[i] = newrecnum; rc = tdbio_write_record( &rec ); if( rc ) log_error( "upd_hashtable: write hlst failed: %s\n", g10_errstr(rc) ); return rc; /* done */ } } if( rec.r.hlst.next ) { rc = tdbio_read_record( rec.r.hlst.next, &rec, RECTYPE_HLST ); if( rc ) { log_error( "upd_hashtable: read hlst failed: %s\n", g10_errstr(rc) ); return rc; } } else { /* add a new list record */ rec.r.hlst.next = item = tdbio_new_recnum(); rc = tdbio_write_record( &rec ); if( rc ) { log_error( "upd_hashtable: write hlst failed: %s\n", g10_errstr(rc) ); return rc; } memset( &rec, 0, sizeof rec ); rec.rectype = RECTYPE_HLST; rec.recnum = item; rec.r.hlst.rnum[0] = newrecnum; rc = tdbio_write_record( &rec ); if( rc ) log_error( "upd_hashtable: write ext hlst failed: %s\n", g10_errstr(rc) ); return rc; /* done */ } } /* end loop over hlst slots */ } else if( rec.rectype == RECTYPE_TRUST ) { /* insert a list record */ if( rec.recnum == newrecnum ) { return 0; } item = rec.recnum; /* save number of key record */ memset( &rec, 0, sizeof rec ); rec.rectype = RECTYPE_HLST; rec.recnum = tdbio_new_recnum(); rec.r.hlst.rnum[0] = item; /* old keyrecord */ rec.r.hlst.rnum[1] = newrecnum; /* and new one */ rc = tdbio_write_record( &rec ); if( rc ) { log_error( "upd_hashtable: write new hlst failed: %s\n", g10_errstr(rc) ); return rc; } /* update the hashtable record */ lastrec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = rec.recnum; rc = tdbio_write_record( &lastrec ); if( rc ) log_error( "upd_hashtable: update htbl failed: %s\n", g10_errstr(rc) ); return rc; /* ready */ } else { log_error( "hashtbl %lu: %lu/%d points to an invalid record %lu\n", table, hashrec, (msb % ITEMS_PER_HTBL_RECORD), item); list_trustdb(NULL); return G10ERR_TRUSTDB; } } return 0; } /**************** * Drop an entry from a hashtable * table gives the start of the table, key and keylen is the key, */ static int drop_from_hashtable( ulong table, byte *key, int keylen, ulong recnum ) { TRUSTREC rec; ulong hashrec, item; int msb; int level=0; int rc, i; hashrec = table; next_level: msb = key[level]; hashrec += msb / ITEMS_PER_HTBL_RECORD; rc = tdbio_read_record( hashrec, &rec, RECTYPE_HTBL ); if( rc ) { log_error("drop_from_hashtable: read failed: %s\n", g10_errstr(rc) ); return rc; } item = rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD]; if( !item ) /* not found - forget about it */ return 0; if( item == recnum ) { /* tables points direct to the record */ rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = 0; rc = tdbio_write_record( &rec ); if( rc ) log_error("drop_from_hashtable: write htbl failed: %s\n", g10_errstr(rc) ); return rc; } rc = tdbio_read_record( item, &rec, 0 ); if( rc ) { log_error( "drop_from_hashtable: read item failed: %s\n", g10_errstr(rc) ); return rc; } if( rec.rectype == RECTYPE_HTBL ) { hashrec = item; level++; if( level >= keylen ) { log_error( "hashtable has invalid indirections.\n"); return G10ERR_TRUSTDB; } goto next_level; } if( rec.rectype == RECTYPE_HLST ) { for(;;) { for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { if( rec.r.hlst.rnum[i] == recnum ) { rec.r.hlst.rnum[i] = 0; /* drop */ rc = tdbio_write_record( &rec ); if( rc ) log_error("drop_from_hashtable: write htbl failed: %s\n", g10_errstr(rc) ); return rc; } } if( rec.r.hlst.next ) { rc = tdbio_read_record( rec.r.hlst.next, &rec, RECTYPE_HLST); if( rc ) { log_error( "drop_from_hashtable: read hlst failed: %s\n", g10_errstr(rc) ); return rc; } } else return 0; /* key not in table */ } } log_error( "hashtbl %lu: %lu/%d points to wrong record %lu\n", table, hashrec, (msb % ITEMS_PER_HTBL_RECORD), item); return G10ERR_TRUSTDB; } /**************** * Lookup a record via the hashtable tablewith key/keylen and return the * result in rec. cmp() should return if the record is the desired one. * Returns -1 if not found, 0 if found or another errocode */ static int lookup_hashtable( ulong table, const byte *key, size_t keylen, int (*cmpfnc)(const void*, const TRUSTREC *), const void *cmpdata, TRUSTREC *rec ) { int rc; ulong hashrec, item; int msb; int level=0; hashrec = table; next_level: msb = key[level]; hashrec += msb / ITEMS_PER_HTBL_RECORD; rc = tdbio_read_record( hashrec, rec, RECTYPE_HTBL ); if( rc ) { log_error("lookup_hashtable failed: %s\n", g10_errstr(rc) ); return rc; } item = rec->r.htbl.item[msb % ITEMS_PER_HTBL_RECORD]; if( !item ) return -1; /* not found */ rc = tdbio_read_record( item, rec, 0 ); if( rc ) { log_error( "hashtable read failed: %s\n", g10_errstr(rc) ); return rc; } if( rec->rectype == RECTYPE_HTBL ) { hashrec = item; level++; if( level >= keylen ) { log_error("hashtable has invalid indirections\n"); return G10ERR_TRUSTDB; } goto next_level; } else if( rec->rectype == RECTYPE_HLST ) { for(;;) { int i; for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { if( rec->r.hlst.rnum[i] ) { TRUSTREC tmp; rc = tdbio_read_record( rec->r.hlst.rnum[i], &tmp, 0 ); if( rc ) { log_error( "lookup_hashtable: read item failed: %s\n", g10_errstr(rc) ); return rc; } if( (*cmpfnc)( cmpdata, &tmp ) ) { *rec = tmp; return 0; } } } if( rec->r.hlst.next ) { rc = tdbio_read_record( rec->r.hlst.next, rec, RECTYPE_HLST ); if( rc ) { log_error( "lookup_hashtable: read hlst failed: %s\n", g10_errstr(rc) ); return rc; } } else return -1; /* not found */ } } if( (*cmpfnc)( cmpdata, rec ) ) return 0; /* really found */ return -1; /* no: not found */ } /**************** * Update the trust hashtbl or create the table if it does not exist */ static int update_trusthashtbl( TRUSTREC *tr ) { return upd_hashtable( get_trusthashrec(), tr->r.trust.fingerprint, 20, tr->recnum ); } void tdbio_dump_record( TRUSTREC *rec, FILE *fp ) { int i; ulong rnum = rec->recnum; fprintf(fp, "rec %5lu, ", rnum ); switch( rec->rectype ) { case 0: fprintf(fp, "blank\n"); break; case RECTYPE_VER: fprintf(fp, "version, td=%lu, f=%lu, m/c/d=%d/%d/%d tm=%d mcl=%d nc=%lu (%s)\n", rec->r.ver.trusthashtbl, rec->r.ver.firstfree, rec->r.ver.marginals, rec->r.ver.completes, rec->r.ver.cert_depth, rec->r.ver.trust_model, rec->r.ver.min_cert_level, rec->r.ver.nextcheck, strtimestamp(rec->r.ver.nextcheck) ); break; case RECTYPE_FREE: fprintf(fp, "free, next=%lu\n", rec->r.free.next ); break; case RECTYPE_HTBL: fprintf(fp, "htbl,"); for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) fprintf(fp, " %lu", rec->r.htbl.item[i] ); putc('\n', fp); break; case RECTYPE_HLST: fprintf(fp, "hlst, next=%lu,", rec->r.hlst.next ); for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) fprintf(fp, " %lu", rec->r.hlst.rnum[i] ); putc('\n', fp); break; case RECTYPE_TRUST: fprintf(fp, "trust "); for(i=0; i < 20; i++ ) fprintf(fp, "%02X", rec->r.trust.fingerprint[i] ); fprintf (fp, ", ot=%d, d=%d, vl=%lu\n", rec->r.trust.ownertrust, rec->r.trust.depth, rec->r.trust.validlist); break; case RECTYPE_VALID: fprintf(fp, "valid "); for(i=0; i < 20; i++ ) fprintf(fp, "%02X", rec->r.valid.namehash[i] ); fprintf (fp, ", v=%d, next=%lu\n", rec->r.valid.validity, rec->r.valid.next); break; default: fprintf(fp, "unknown type %d\n", rec->rectype ); break; } } /**************** * read the record with number recnum * returns: -1 on error, 0 on success */ int tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected ) { byte readbuf[TRUST_RECORD_LEN]; const byte *buf, *p; int rc = 0; int n, i; if( db_fd == -1 ) open_db(); buf = get_record_from_cache( recnum ); if( !buf ) { if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) { log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) ); return G10ERR_READ_FILE; } n = read( db_fd, readbuf, TRUST_RECORD_LEN); if( !n ) { return -1; /* eof */ } else if( n != TRUST_RECORD_LEN ) { log_error(_("trustdb: read failed (n=%d): %s\n"), n, strerror(errno) ); return G10ERR_READ_FILE; } buf = readbuf; } rec->recnum = recnum; rec->dirty = 0; p = buf; rec->rectype = *p++; if( expected && rec->rectype != expected ) { log_error("%lu: read expected rec type %d, got %d\n", recnum, expected, rec->rectype ); return G10ERR_TRUSTDB; } p++; /* skip reserved byte */ switch( rec->rectype ) { case 0: /* unused (free) record */ break; case RECTYPE_VER: /* version record */ if( memcmp(buf+1, "gpg", 3 ) ) { log_error( _("%s: not a trustdb file\n"), db_name ); rc = G10ERR_TRUSTDB; } p += 2; /* skip "gpg" */ rec->r.ver.version = *p++; rec->r.ver.marginals = *p++; rec->r.ver.completes = *p++; rec->r.ver.cert_depth = *p++; rec->r.ver.trust_model = *p++; rec->r.ver.min_cert_level = *p++; p += 2; - rec->r.ver.created = buftoulong(p); p += 4; - rec->r.ver.nextcheck = buftoulong(p); p += 4; + rec->r.ver.created = buf32_to_ulong (p); p += 4; + rec->r.ver.nextcheck = buf32_to_ulong (p); p += 4; p += 4; p += 4; - rec->r.ver.firstfree =buftoulong(p); p += 4; + rec->r.ver.firstfree =buf32_to_ulong (p); p += 4; p += 4; - rec->r.ver.trusthashtbl =buftoulong(p); p += 4; + rec->r.ver.trusthashtbl =buf32_to_ulong (p); p += 4; if( recnum ) { log_error( _("%s: version record with recnum %lu\n"), db_name, (ulong)recnum ); rc = G10ERR_TRUSTDB; } else if( rec->r.ver.version != 3 ) { log_error( _("%s: invalid file version %d\n"), db_name, rec->r.ver.version ); rc = G10ERR_TRUSTDB; } break; case RECTYPE_FREE: - rec->r.free.next = buftoulong(p); p += 4; + rec->r.free.next = buf32_to_ulong (p); p += 4; break; case RECTYPE_HTBL: for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) { - rec->r.htbl.item[i] = buftoulong(p); p += 4; + rec->r.htbl.item[i] = buf32_to_ulong (p); p += 4; } break; case RECTYPE_HLST: - rec->r.hlst.next = buftoulong(p); p += 4; + rec->r.hlst.next = buf32_to_ulong (p); p += 4; for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { - rec->r.hlst.rnum[i] = buftoulong(p); p += 4; + rec->r.hlst.rnum[i] = buf32_to_ulong (p); p += 4; } break; case RECTYPE_TRUST: memcpy( rec->r.trust.fingerprint, p, 20); p+=20; rec->r.trust.ownertrust = *p++; rec->r.trust.depth = *p++; rec->r.trust.min_ownertrust = *p++; p++; - rec->r.trust.validlist = buftoulong(p); p += 4; + rec->r.trust.validlist = buf32_to_ulong (p); p += 4; break; case RECTYPE_VALID: memcpy( rec->r.valid.namehash, p, 20); p+=20; rec->r.valid.validity = *p++; - rec->r.valid.next = buftoulong(p); p += 4; + rec->r.valid.next = buf32_to_ulong (p); p += 4; rec->r.valid.full_count = *p++; rec->r.valid.marginal_count = *p++; break; default: log_error( "%s: invalid record type %d at recnum %lu\n", db_name, rec->rectype, (ulong)recnum ); rc = G10ERR_TRUSTDB; break; } return rc; } /**************** * Write the record at RECNUM */ int tdbio_write_record( TRUSTREC *rec ) { byte buf[TRUST_RECORD_LEN], *p; int rc = 0; int i; ulong recnum = rec->recnum; if( db_fd == -1 ) open_db(); memset(buf, 0, TRUST_RECORD_LEN); p = buf; *p++ = rec->rectype; p++; switch( rec->rectype ) { case 0: /* unused record */ break; case RECTYPE_VER: /* version record */ if( recnum ) BUG(); memcpy(p-1, "gpg", 3 ); p += 2; *p++ = rec->r.ver.version; *p++ = rec->r.ver.marginals; *p++ = rec->r.ver.completes; *p++ = rec->r.ver.cert_depth; *p++ = rec->r.ver.trust_model; *p++ = rec->r.ver.min_cert_level; p += 2; ulongtobuf(p, rec->r.ver.created); p += 4; ulongtobuf(p, rec->r.ver.nextcheck); p += 4; p += 4; p += 4; ulongtobuf(p, rec->r.ver.firstfree ); p += 4; p += 4; ulongtobuf(p, rec->r.ver.trusthashtbl ); p += 4; break; case RECTYPE_FREE: ulongtobuf(p, rec->r.free.next); p += 4; break; case RECTYPE_HTBL: for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) { ulongtobuf( p, rec->r.htbl.item[i]); p += 4; } break; case RECTYPE_HLST: ulongtobuf( p, rec->r.hlst.next); p += 4; for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) { ulongtobuf( p, rec->r.hlst.rnum[i]); p += 4; } break; case RECTYPE_TRUST: memcpy( p, rec->r.trust.fingerprint, 20); p += 20; *p++ = rec->r.trust.ownertrust; *p++ = rec->r.trust.depth; *p++ = rec->r.trust.min_ownertrust; p++; ulongtobuf( p, rec->r.trust.validlist); p += 4; break; case RECTYPE_VALID: memcpy( p, rec->r.valid.namehash, 20); p += 20; *p++ = rec->r.valid.validity; ulongtobuf( p, rec->r.valid.next); p += 4; *p++ = rec->r.valid.full_count; *p++ = rec->r.valid.marginal_count; break; default: BUG(); } rc = put_record_into_cache( recnum, buf ); if( rc ) ; else if( rec->rectype == RECTYPE_TRUST ) rc = update_trusthashtbl( rec ); return rc; } int tdbio_delete_record( ulong recnum ) { TRUSTREC vr, rec; int rc; /* Must read the record fist, so we can drop it from the hash tables */ rc = tdbio_read_record( recnum, &rec, 0 ); if( rc ) ; else if( rec.rectype == RECTYPE_TRUST ) { rc = drop_from_hashtable( get_trusthashrec(), rec.r.trust.fingerprint, 20, rec.recnum ); } if( rc ) return rc; /* now we can chnage it to a free record */ rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, g10_errstr(rc) ); rec.recnum = recnum; rec.rectype = RECTYPE_FREE; rec.r.free.next = vr.r.ver.firstfree; vr.r.ver.firstfree = recnum; rc = tdbio_write_record( &rec ); if( !rc ) rc = tdbio_write_record( &vr ); return rc; } /**************** * create a new record and return its record number */ ulong tdbio_new_recnum() { off_t offset; ulong recnum; TRUSTREC vr, rec; int rc; /* look for unused records */ rc = tdbio_read_record( 0, &vr, RECTYPE_VER ); if( rc ) log_fatal( _("%s: error reading version record: %s\n"), db_name, g10_errstr(rc) ); if( vr.r.ver.firstfree ) { recnum = vr.r.ver.firstfree; rc = tdbio_read_record( recnum, &rec, RECTYPE_FREE ); if( rc ) { log_error( _("%s: error reading free record: %s\n"), db_name, g10_errstr(rc) ); return rc; } /* update dir record */ vr.r.ver.firstfree = rec.r.free.next; rc = tdbio_write_record( &vr ); if( rc ) { log_error( _("%s: error writing dir record: %s\n"), db_name, g10_errstr(rc) ); return rc; } /*zero out the new record */ memset( &rec, 0, sizeof rec ); rec.rectype = 0; /* unused record */ rec.recnum = recnum; rc = tdbio_write_record( &rec ); if( rc ) log_fatal(_("%s: failed to zero a record: %s\n"), db_name, g10_errstr(rc)); } else { /* not found, append a new record */ offset = lseek( db_fd, 0, SEEK_END ); if( offset == -1 ) log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) ); recnum = offset / TRUST_RECORD_LEN; assert(recnum); /* this is will never be the first record */ /* we must write a record, so that the next call to this function * returns another recnum */ memset( &rec, 0, sizeof rec ); rec.rectype = 0; /* unused record */ rec.recnum = recnum; rc = 0; if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) { log_error(_("trustdb rec %lu: lseek failed: %s\n"), recnum, strerror(errno) ); rc = G10ERR_WRITE_FILE; } else { int n = write( db_fd, &rec, TRUST_RECORD_LEN); if( n != TRUST_RECORD_LEN ) { log_error(_("trustdb rec %lu: write failed (n=%d): %s\n"), recnum, n, strerror(errno) ); rc = G10ERR_WRITE_FILE; } } if( rc ) log_fatal(_("%s: failed to append a record: %s\n"), db_name, g10_errstr(rc)); } return recnum ; } static int cmp_trec_fpr (const void *fpr, const TRUSTREC *rec ) { return (rec->rectype == RECTYPE_TRUST && !memcmp( rec->r.trust.fingerprint, fpr, 20)); } int tdbio_search_trust_byfpr( const byte *fingerprint, TRUSTREC *rec ) { int rc; /* Locate the trust record using the hash table. */ rc = lookup_hashtable (get_trusthashrec(), fingerprint, 20, cmp_trec_fpr, fingerprint, rec); return rc; } int tdbio_search_trust_bypk (PKT_public_key *pk, TRUSTREC *rec) { byte fingerprint[MAX_FINGERPRINT_LEN]; size_t fingerlen; fingerprint_from_pk( pk, fingerprint, &fingerlen ); for (; fingerlen < 20; fingerlen++ ) fingerprint[fingerlen] = 0; return tdbio_search_trust_byfpr (fingerprint, rec); } void tdbio_invalid(void) { log_error (_("Error: The trustdb is corrupted.\n")); how_to_fix_the_trustdb (); g10_exit (2); } /* * Migrate the trustdb as just up to gpg 1.0.6 (trustdb version 2) * to the 2.1 version as used with 1.0.6b - This is pretty trivial as needs * only to scan the tdb and insert new the new trust records. The old ones are * obsolte from now on */ static void migrate_from_v2 () { TRUSTREC rec; int i, n; struct { ulong keyrecno; byte ot; byte okay; byte fpr[20]; } *ottable; int ottable_size, ottable_used; byte oldbuf[40]; ulong recno; int rc, count; ottable_size = 5; ottable = xmalloc (ottable_size * sizeof *ottable); ottable_used = 0; /* We have some restrictions here. We can't use the version record * and we can't use any of the old hashtables because we dropped the * code. So we first collect all ownertrusts and then use a second * pass fo find the associated keys. We have to do this all without using * the regular record read functions. */ /* get all the ownertrusts */ if (lseek (db_fd, 0, SEEK_SET ) == -1 ) log_fatal ("migrate_from_v2: lseek failed: %s\n", strerror (errno)); for (recno=0;;recno++) { do n = read (db_fd, oldbuf, 40); while (n==-1 && errno == EINTR); if (!n) break; /* eof */ if (n != 40) log_fatal ("migrate_vfrom_v2: read error or short read\n"); if (*oldbuf != 2) continue; /* v2 dir record */ if (ottable_used == ottable_size) { ottable_size += 1000; ottable = xrealloc (ottable, ottable_size * sizeof *ottable); } - ottable[ottable_used].keyrecno = buftoulong (oldbuf+6); + ottable[ottable_used].keyrecno = buf32_to_ulong (oldbuf+6); ottable[ottable_used].ot = oldbuf[18]; ottable[ottable_used].okay = 0; memset (ottable[ottable_used].fpr,0, 20); if (ottable[ottable_used].keyrecno && ottable[ottable_used].ot) ottable_used++; } log_info ("found %d ownertrust records\n", ottable_used); /* Read again and find the fingerprints */ if (lseek (db_fd, 0, SEEK_SET ) == -1 ) log_fatal ("migrate_from_v2: lseek failed: %s\n", strerror (errno)); for (recno=0;;recno++) { do n = read (db_fd, oldbuf, 40); while (n==-1 && errno == EINTR); if (!n) break; /* eof */ if (n != 40) log_fatal ("migrate_from_v2: read error or short read\n"); if (*oldbuf != 3) continue; /* v2 key record */ for (i=0; i < ottable_used; i++) { if (ottable[i].keyrecno == recno) { memcpy (ottable[i].fpr, oldbuf+20, 20); ottable[i].okay = 1; break; } } } /* got everything - create the v3 trustdb */ if (ftruncate (db_fd, 0)) log_fatal ("can't truncate `%s': %s\n", db_name, strerror (errno) ); if (create_version_record ()) log_fatal ("failed to recreate version record of `%s'\n", db_name); /* access the hash table, so it is store just after the version record, * this is not needed put a dump is more pretty */ get_trusthashrec (); /* And insert the old ownertrust values */ count = 0; for (i=0; i < ottable_used; i++) { if (!ottable[i].okay) continue; memset (&rec, 0, sizeof rec); rec.recnum = tdbio_new_recnum (); rec.rectype = RECTYPE_TRUST; memcpy(rec.r.trust.fingerprint, ottable[i].fpr, 20); rec.r.trust.ownertrust = ottable[i].ot; if (tdbio_write_record (&rec)) log_fatal ("failed to write trust record of `%s'\n", db_name); count++; } revalidation_mark (); rc = tdbio_sync (); if (rc) log_fatal ("failed to sync `%s'\n", db_name); log_info ("migrated %d version 2 ownertrusts\n", count); xfree (ottable); } diff --git a/g10/trustdb.c b/g10/trustdb.c index a54110653..e4317e2f5 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -1,2529 +1,2529 @@ /* trustdb.c * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008, 2012 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 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #ifndef DISABLE_REGEX #include #ifdef USE_INTERNAL_REGEX #include "_regex.h" #else #include #endif #endif /* !DISABLE_REGEX */ #include "errors.h" #include "iobuf.h" #include "keydb.h" #include "memory.h" #include "util.h" #include "options.h" #include "packet.h" #include "main.h" #include "i18n.h" #include "tdbio.h" #include "trustdb.h" /* * A structure to store key identification as well as some stuff needed * for validation */ struct key_item { struct key_item *next; unsigned int ownertrust,min_ownertrust; byte trust_depth; byte trust_value; char *trust_regexp; u32 kid[2]; }; typedef struct key_item **KeyHashTable; /* see new_key_hash_table() */ /* * Structure to keep track of keys, this is used as an array wherre * the item right after the last one has a keyblock set to NULL. * Maybe we can drop this thing and replace it by key_item */ struct key_array { KBNODE keyblock; }; /* control information for the trust DB */ static struct { int init; int level; char *dbname; int no_trustdb; /* Set if a trustdb file is not available. */ } trustdb_args; /* some globals */ static struct key_item *user_utk_list; /* temp. used to store --trusted-keys */ static struct key_item *utk_list; /* all ultimately trusted keys */ static int pending_check_trustdb; static int validate_keys (int interactive); /********************************************** ************* some helpers ******************* **********************************************/ static struct key_item * new_key_item (void) { struct key_item *k; k = xmalloc_clear (sizeof *k); return k; } static void release_key_items (struct key_item *k) { struct key_item *k2; for (; k; k = k2) { k2 = k->next; xfree (k->trust_regexp); xfree (k); } } /* * For fast keylook up we need a hash table. Each byte of a KeyIDs * should be distributed equally over the 256 possible values (except * for v3 keyIDs but we consider them as not important here). So we * can just use 10 bits to index a table of 1024 key items. * Possible optimization: Don not use key_items but other hash_table when the * duplicates lists gets too large. */ static KeyHashTable new_key_hash_table (void) { struct key_item **tbl; tbl = xmalloc_clear (1024 * sizeof *tbl); return tbl; } static void release_key_hash_table (KeyHashTable tbl) { int i; if (!tbl) return; for (i=0; i < 1024; i++) release_key_items (tbl[i]); xfree (tbl); } /* * Returns: True if the keyID is in the given hash table */ static int test_key_hash_table (KeyHashTable tbl, u32 *kid) { struct key_item *k; for (k = tbl[(kid[1] & 0x03ff)]; k; k = k->next) if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) return 1; return 0; } /* * Add a new key to the hash table. The key is identified by its key ID. */ static void add_key_hash_table (KeyHashTable tbl, u32 *kid) { struct key_item *k, *kk; for (k = tbl[(kid[1] & 0x03ff)]; k; k = k->next) if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) return; /* already in table */ kk = new_key_item (); kk->kid[0] = kid[0]; kk->kid[1] = kid[1]; kk->next = tbl[(kid[1] & 0x03ff)]; tbl[(kid[1] & 0x03ff)] = kk; } /* * Release a key_array */ static void release_key_array ( struct key_array *keys ) { struct key_array *k; if (keys) { for (k=keys; k->keyblock; k++) release_kbnode (k->keyblock); xfree (keys); } } /********************************************* ********** Initialization ***************** *********************************************/ /* * Used to register extra ultimately trusted keys - this has to be done * before initializing the validation module. * FIXME: Should be replaced by a function to add those keys to the trustdb. */ void register_trusted_keyid(u32 *keyid) { struct key_item *k; k = new_key_item (); k->kid[0] = keyid[0]; k->kid[1] = keyid[1]; k->next = user_utk_list; user_utk_list = k; } void register_trusted_key( const char *string ) { KEYDB_SEARCH_DESC desc; if (classify_user_id (string, &desc) != KEYDB_SEARCH_MODE_LONG_KID ) { log_error(_("`%s' is not a valid long keyID\n"), string ); return; } register_trusted_keyid(desc.u.kid); } /* * Helper to add a key to the global list of ultimately trusted keys. * Retruns: true = inserted, false = already in in list. */ static int add_utk (u32 *kid) { struct key_item *k; for (k = utk_list; k; k = k->next) { if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) { return 0; } } k = new_key_item (); k->kid[0] = kid[0]; k->kid[1] = kid[1]; k->ownertrust = TRUST_ULTIMATE; k->next = utk_list; utk_list = k; if( opt.verbose > 1 ) log_info(_("key %s: accepted as trusted key\n"), keystr(kid)); return 1; } /**************** * Verify that all our secret keys are usable and put them into the utk_list. */ static void verify_own_keys(void) { TRUSTREC rec; ulong recnum; int rc; struct key_item *k; if (utk_list) return; /* scan the trustdb to find all ultimately trusted keys */ for (recnum=1; !tdbio_read_record (recnum, &rec, 0); recnum++ ) { if ( rec.rectype == RECTYPE_TRUST && (rec.r.trust.ownertrust & TRUST_MASK) == TRUST_ULTIMATE) { byte *fpr = rec.r.trust.fingerprint; int fprlen; u32 kid[2]; /* Problem: We do only use fingerprints in the trustdb but * we need the keyID here to indetify the key; we can only * use that ugly hack to distinguish between 16 and 20 * butes fpr - it does not work always so we better change * the whole validation code to only work with * fingerprints */ fprlen = (!fpr[16] && !fpr[17] && !fpr[18] && !fpr[19])? 16:20; keyid_from_fingerprint (fpr, fprlen, kid); if (!add_utk (kid)) log_info(_("key %s occurs more than once in the trustdb\n"), keystr(kid)); } } /* Put any --trusted-key keys into the trustdb */ for (k = user_utk_list; k; k = k->next) { if ( add_utk (k->kid) ) { /* not yet in trustDB as ultimately trusted */ PKT_public_key pk; memset (&pk, 0, sizeof pk); rc = get_pubkey (&pk, k->kid); if (rc) log_info(_("key %s: no public key for trusted key - skipped\n"), keystr(k->kid)); else { update_ownertrust (&pk, ((get_ownertrust (&pk) & ~TRUST_MASK) | TRUST_ULTIMATE )); release_public_key_parts (&pk); } log_info (_("key %s marked as ultimately trusted\n"),keystr(k->kid)); } } /* release the helper table table */ release_key_items (user_utk_list); user_utk_list = NULL; return; } /********************************************* *********** TrustDB stuff ******************* *********************************************/ /* * Read a record but die if it does not exist */ static void read_record (ulong recno, TRUSTREC *rec, int rectype ) { int rc = tdbio_read_record (recno, rec, rectype); if (rc) { log_error(_("trust record %lu, req type %d: read failed: %s\n"), recno, rec->rectype, g10_errstr(rc) ); tdbio_invalid(); } if (rectype != rec->rectype) { log_error(_("trust record %lu is not of requested type %d\n"), rec->recnum, rectype); tdbio_invalid(); } } /* * Write a record and die on error */ static void write_record (TRUSTREC *rec) { int rc = tdbio_write_record (rec); if (rc) { log_error(_("trust record %lu, type %d: write failed: %s\n"), rec->recnum, rec->rectype, g10_errstr(rc) ); tdbio_invalid(); } } /* * sync the TrustDb and die on error */ static void do_sync(void) { int rc = tdbio_sync (); if(rc) { log_error (_("trustdb: sync failed: %s\n"), g10_errstr(rc) ); g10_exit(2); } } static const char * trust_model_string(void) { switch(opt.trust_model) { case TM_CLASSIC: return "classic"; case TM_PGP: return "PGP"; case TM_EXTERNAL: return "external"; case TM_ALWAYS: return "always"; case TM_DIRECT: return "direct"; default: return "unknown"; } } /**************** * Perform some checks over the trustdb * level 0: only open the db * 1: used for initial program startup */ int setup_trustdb( int level, const char *dbname ) { /* just store the args */ if( trustdb_args.init ) return 0; trustdb_args.level = level; trustdb_args.dbname = dbname? xstrdup(dbname): NULL; return 0; } void how_to_fix_the_trustdb () { const char *name = trustdb_args.dbname; if (!name) name = "trustdb.gpg"; log_info (_("You may try to re-create the trustdb using the commands:\n")); log_info (" cd %s\n", default_homedir ()); log_info (" gpg2 --export-ownertrust > otrust.tmp\n"); #ifdef HAVE_W32_SYSTEM log_info (" del %s\n", name); #else log_info (" rm %s\n", name); #endif log_info (" gpg2 --import-ownertrust < otrust.tmp\n"); log_info (_("If that does not work, please consult the manual\n")); } void init_trustdb() { int level = trustdb_args.level; const char* dbname = trustdb_args.dbname; if( trustdb_args.init ) return; trustdb_args.init = 1; if(level==0 || level==1) { int rc = tdbio_set_dbname (dbname, !!level, &trustdb_args.no_trustdb); if( rc ) log_fatal("can't init trustdb: %s\n", g10_errstr(rc) ); } else BUG(); if(opt.trust_model==TM_AUTO) { /* Try and set the trust model off of whatever the trustdb says it is. */ opt.trust_model=tdbio_read_model(); /* Sanity check this ;) */ if(opt.trust_model!=TM_CLASSIC && opt.trust_model!=TM_PGP && opt.trust_model!=TM_EXTERNAL) { log_info(_("unable to use unknown trust model (%d) - " "assuming %s trust model\n"),opt.trust_model,"PGP"); opt.trust_model=TM_PGP; } if(opt.verbose) log_info(_("using %s trust model\n"),trust_model_string()); } if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC) { /* Verify the list of ultimately trusted keys and move the --trusted-keys list there as well. */ if(level==1) verify_own_keys(); if(!tdbio_db_matches_options()) pending_check_trustdb=1; } } /*********************************************** ************* Print helpers **************** ***********************************************/ /**************** * This function returns a letter for a trustvalue Trust flags * are ignore. */ static int trust_letter (unsigned int value) { switch( (value & TRUST_MASK) ) { case TRUST_UNKNOWN: return '-'; case TRUST_EXPIRED: return 'e'; case TRUST_UNDEFINED: return 'q'; case TRUST_NEVER: return 'n'; case TRUST_MARGINAL: return 'm'; case TRUST_FULLY: return 'f'; case TRUST_ULTIMATE: return 'u'; default: return '?'; } } const char * uid_trust_string_fixed(PKT_public_key *key,PKT_user_id *uid) { if(!key && !uid) /* TRANSLATORS: these strings are similar to those in trust_value_to_string(), but are a fixed length. This is needed to make attractive information listings where columns line up properly. The value "10" should be the length of the strings you choose to translate to. This is the length in printable columns. It gets passed to atoi() so everything after the number is essentially a comment and need not be translated. Either key and uid are both NULL, or neither are NULL. */ return _("10 translator see trustdb.c:uid_trust_string_fixed"); else if(uid->is_revoked || (key && key->is_revoked)) return _("[ revoked]"); else if(uid->is_expired) return _("[ expired]"); else if(key) switch(get_validity(key,uid)&TRUST_MASK) { case TRUST_UNKNOWN: return _("[ unknown]"); case TRUST_EXPIRED: return _("[ expired]"); case TRUST_UNDEFINED: return _("[ undef ]"); case TRUST_MARGINAL: return _("[marginal]"); case TRUST_FULLY: return _("[ full ]"); case TRUST_ULTIMATE: return _("[ultimate]"); } return "err"; } /* The strings here are similar to those in pkclist.c:do_edit_ownertrust() */ const char * trust_value_to_string (unsigned int value) { switch( (value & TRUST_MASK) ) { case TRUST_UNKNOWN: return _("unknown"); case TRUST_EXPIRED: return _("expired"); case TRUST_UNDEFINED: return _("undefined"); case TRUST_NEVER: return _("never"); case TRUST_MARGINAL: return _("marginal"); case TRUST_FULLY: return _("full"); case TRUST_ULTIMATE: return _("ultimate"); default: return "err"; } } int string_to_trust_value (const char *str) { if(ascii_strcasecmp(str,"undefined")==0) return TRUST_UNDEFINED; else if(ascii_strcasecmp(str,"never")==0) return TRUST_NEVER; else if(ascii_strcasecmp(str,"marginal")==0) return TRUST_MARGINAL; else if(ascii_strcasecmp(str,"full")==0) return TRUST_FULLY; else if(ascii_strcasecmp(str,"ultimate")==0) return TRUST_ULTIMATE; else return -1; } /**************** * Recreate the WoT but do not ask for new ownertrusts. Special * feature: In batch mode and without a forced yes, this is only done * when a check is due. This can be used to run the check from a crontab */ void check_trustdb () { init_trustdb(); if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC) { if (opt.batch && !opt.answer_yes) { ulong scheduled; scheduled = tdbio_read_nextcheck (); if (!scheduled) { log_info (_("no need for a trustdb check\n")); return; } if (scheduled > make_timestamp ()) { log_info (_("next trustdb check due at %s\n"), strtimestamp (scheduled)); return; } } validate_keys (0); } else log_info (_("no need for a trustdb check with `%s' trust model\n"), trust_model_string()); } /* * Recreate the WoT. */ void update_trustdb() { init_trustdb(); if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC) validate_keys (1); else log_info (_("no need for a trustdb update with `%s' trust model\n"), trust_model_string()); } void revalidation_mark (void) { init_trustdb(); if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return; /* we simply set the time for the next check to 1 (far back in 1970) * so that a --update-trustdb will be scheduled */ if (tdbio_write_nextcheck (1)) do_sync (); pending_check_trustdb = 1; } int trustdb_pending_check(void) { return pending_check_trustdb; } /* If the trustdb is dirty, and we're interactive, update it. Otherwise, check it unless no-auto-check-trustdb is set. */ void trustdb_check_or_update(void) { if(trustdb_pending_check()) { if(opt.interactive) update_trustdb(); else if(!opt.no_auto_check_trustdb) check_trustdb(); } } void read_trust_options(byte *trust_model,ulong *created,ulong *nextcheck, byte *marginals,byte *completes,byte *cert_depth, byte *min_cert_level) { TRUSTREC opts; init_trustdb(); if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) memset (&opts, 0, sizeof opts); else read_record(0,&opts,RECTYPE_VER); if(trust_model) *trust_model=opts.r.ver.trust_model; if(created) *created=opts.r.ver.created; if(nextcheck) *nextcheck=opts.r.ver.nextcheck; if(marginals) *marginals=opts.r.ver.marginals; if(completes) *completes=opts.r.ver.completes; if(cert_depth) *cert_depth=opts.r.ver.cert_depth; if(min_cert_level) *min_cert_level=opts.r.ver.min_cert_level; } /*********************************************** *********** Ownertrust et al. **************** ***********************************************/ static int read_trust_record (PKT_public_key *pk, TRUSTREC *rec) { int rc; init_trustdb(); rc = tdbio_search_trust_bypk (pk, rec); if (rc == -1) return -1; /* no record yet */ if (rc) { log_error ("trustdb: searching trust record failed: %s\n", g10_errstr (rc)); return rc; } if (rec->rectype != RECTYPE_TRUST) { log_error ("trustdb: record %lu is not a trust record\n", rec->recnum); return G10ERR_TRUSTDB; } return 0; } /**************** * Return the assigned ownertrust value for the given public key. * The key should be the primary key. */ unsigned int get_ownertrust ( PKT_public_key *pk) { TRUSTREC rec; int rc; if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return TRUST_UNKNOWN; rc = read_trust_record (pk, &rec); if (rc == -1) return TRUST_UNKNOWN; /* no record yet */ if (rc) { tdbio_invalid (); return rc; /* actually never reached */ } return rec.r.trust.ownertrust; } unsigned int get_min_ownertrust (PKT_public_key *pk) { TRUSTREC rec; int rc; if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return TRUST_UNKNOWN; rc = read_trust_record (pk, &rec); if (rc == -1) return TRUST_UNKNOWN; /* no record yet */ if (rc) { tdbio_invalid (); return rc; /* actually never reached */ } return rec.r.trust.min_ownertrust; } /* * Same as get_ownertrust but this takes the minimum ownertrust value * into into account, and will bump up the value as needed. */ static int get_ownertrust_with_min (PKT_public_key *pk) { unsigned int otrust,otrust_min; otrust = (get_ownertrust (pk) & TRUST_MASK); otrust_min = get_min_ownertrust (pk); if(otrustnamehash, 20) ) break; recno = vrec.r.valid.next; } if (!recno) /* insert a new validity record */ { memset (&vrec, 0, sizeof vrec); vrec.recnum = tdbio_new_recnum (); vrec.rectype = RECTYPE_VALID; memcpy (vrec.r.valid.namehash, uid->namehash, 20); vrec.r.valid.next = trec.r.trust.validlist; trec.r.trust.validlist = vrec.recnum; } vrec.r.valid.validity = validity; vrec.r.valid.full_count = uid->help_full_count; vrec.r.valid.marginal_count = uid->help_marginal_count; write_record (&vrec); trec.r.trust.depth = depth; write_record (&trec); } /*********************************************** ********* Query trustdb values ************** ***********************************************/ /* Return true if key is disabled */ int cache_disabled_value(PKT_public_key *pk) { int rc; TRUSTREC trec; int disabled=0; if(pk->is_disabled) return (pk->is_disabled==2); init_trustdb(); if (trustdb_args.no_trustdb) return 0; /* No trustdb => not disabled. */ rc = read_trust_record (pk, &trec); if (rc && rc != -1) { tdbio_invalid (); goto leave; } if (rc == -1) /* no record found, so assume not disabled */ goto leave; if(trec.r.trust.ownertrust & TRUST_FLAG_DISABLED) disabled=1; /* Cache it for later so we don't need to look at the trustdb every time */ if(disabled) pk->is_disabled=2; else pk->is_disabled=1; leave: return disabled; } void check_trustdb_stale(void) { static int did_nextcheck=0; init_trustdb (); if (trustdb_args.no_trustdb) return; /* No trustdb => can't be stale. */ if (!did_nextcheck && (opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC)) { ulong scheduled; did_nextcheck = 1; scheduled = tdbio_read_nextcheck (); if ((scheduled && scheduled <= make_timestamp ()) || pending_check_trustdb) { if (opt.no_auto_check_trustdb) { pending_check_trustdb = 1; log_info (_("please do a --check-trustdb\n")); } else { log_info (_("checking the trustdb\n")); validate_keys (0); } } } } /* * Return the validity information for PK. If the namehash is not * NULL, the validity of the corresponsing user ID is returned, * otherwise, a reasonable value for the entire key is returned. */ unsigned int get_validity (PKT_public_key *pk, PKT_user_id *uid) { TRUSTREC trec, vrec; int rc; ulong recno; unsigned int validity; u32 kid[2]; PKT_public_key *main_pk; if(uid) namehash_from_uid(uid); init_trustdb (); /* If we have no trustdb (which also means it has not been created) and the trust-model is always, we don't know the validity - return immediately. If we won't do that the tdbio code would try to open the trustdb and run into a fatal error. */ if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return TRUST_UNKNOWN; check_trustdb_stale(); keyid_from_pk (pk, kid); if (pk->main_keyid[0] != kid[0] || pk->main_keyid[1] != kid[1]) { /* this is a subkey - get the mainkey */ main_pk = xmalloc_clear (sizeof *main_pk); rc = get_pubkey (main_pk, pk->main_keyid); if (rc) { char *tempkeystr=xstrdup(keystr(pk->main_keyid)); log_error ("error getting main key %s of subkey %s: %s\n", tempkeystr, keystr(kid), g10_errstr(rc)); xfree(tempkeystr); validity = TRUST_UNKNOWN; goto leave; } } else main_pk = pk; if(opt.trust_model==TM_DIRECT) { /* Note that this happens BEFORE any user ID stuff is checked. The direct trust model applies to keys as a whole. */ validity=get_ownertrust(main_pk); goto leave; } rc = read_trust_record (main_pk, &trec); if (rc && rc != -1) { tdbio_invalid (); return 0; } if (rc == -1) /* no record found */ { validity = TRUST_UNKNOWN; goto leave; } /* loop over all user IDs */ recno = trec.r.trust.validlist; validity = 0; while (recno) { read_record (recno, &vrec, RECTYPE_VALID); if(uid) { /* If a user ID is given we return the validity for that user ID ONLY. If the namehash is not found, then there is no validity at all (i.e. the user ID wasn't signed). */ if(memcmp(vrec.r.valid.namehash,uid->namehash,20)==0) { validity=(vrec.r.valid.validity & TRUST_MASK); break; } } else { /* If no namehash is given, we take the maximum validity over all user IDs */ if ( validity < (vrec.r.valid.validity & TRUST_MASK) ) validity = (vrec.r.valid.validity & TRUST_MASK); } recno = vrec.r.valid.next; } if ( (trec.r.trust.ownertrust & TRUST_FLAG_DISABLED) ) { validity |= TRUST_FLAG_DISABLED; pk->is_disabled=2; } else pk->is_disabled=1; leave: /* set some flags direct from the key */ if (main_pk->is_revoked) validity |= TRUST_FLAG_REVOKED; if (main_pk != pk && pk->is_revoked) validity |= TRUST_FLAG_SUB_REVOKED; /* Note: expiration is a trust value and not a flag - don't know why * I initially designed it that way */ if (main_pk->has_expired || pk->has_expired) validity = (validity & ~TRUST_MASK) | TRUST_EXPIRED; if (pending_check_trustdb) validity |= TRUST_FLAG_PENDING_CHECK; if (main_pk != pk) free_public_key (main_pk); return validity; } int get_validity_info (PKT_public_key *pk, PKT_user_id *uid) { int trustlevel; if (!pk) return '?'; /* Just in case a NULL PK is passed. */ trustlevel = get_validity (pk, uid); if( trustlevel & TRUST_FLAG_REVOKED ) return 'r'; return trust_letter ( trustlevel ); } const char * get_validity_string (PKT_public_key *pk, PKT_user_id *uid) { int trustlevel; if (!pk) return "err"; /* Just in case a NULL PK is passed. */ trustlevel = get_validity (pk, uid); if( trustlevel & TRUST_FLAG_REVOKED ) return _("revoked"); return trust_value_to_string(trustlevel); } static void get_validity_counts (PKT_public_key *pk, PKT_user_id *uid) { TRUSTREC trec, vrec; ulong recno; if(pk==NULL || uid==NULL) BUG(); namehash_from_uid(uid); uid->help_marginal_count=uid->help_full_count=0; init_trustdb (); if(read_trust_record (pk, &trec)!=0) return; /* loop over all user IDs */ recno = trec.r.trust.validlist; while (recno) { read_record (recno, &vrec, RECTYPE_VALID); if(memcmp(vrec.r.valid.namehash,uid->namehash,20)==0) { uid->help_marginal_count=vrec.r.valid.marginal_count; uid->help_full_count=vrec.r.valid.full_count; /* printf("Fetched marginal %d, full %d\n",uid->help_marginal_count,uid->help_full_count); */ break; } recno = vrec.r.valid.next; } } void list_trust_path( const char *username ) { } /**************** * Enumerate all keys, which are needed to build all trust paths for * the given key. This function does not return the key itself or * the ultimate key (the last point in cerificate chain). Only * certificate chains which ends up at an ultimately trusted key * are listed. If ownertrust or validity is not NULL, the corresponding * value for the returned LID is also returned in these variable(s). * * 1) create a void pointer and initialize it to NULL * 2) pass this void pointer by reference to this function. * Set lid to the key you want to enumerate and pass it by reference. * 3) call this function as long as it does not return -1 * to indicate EOF. LID does contain the next key used to build the web * 4) Always call this function a last time with LID set to NULL, * so that it can free its context. * * Returns: -1 on EOF or the level of the returned LID */ int enum_cert_paths( void **context, ulong *lid, unsigned *ownertrust, unsigned *validity ) { return -1; } /**************** * Print the current path */ void enum_cert_paths_print( void **context, FILE *fp, int refresh, ulong selected_lid ) { return; } /**************************************** *********** NEW NEW NEW **************** ****************************************/ static int ask_ownertrust (u32 *kid,int minimum) { PKT_public_key *pk; int rc; int ot; pk = xmalloc_clear (sizeof *pk); rc = get_pubkey (pk, kid); if (rc) { log_error (_("public key %s not found: %s\n"), keystr(kid), g10_errstr(rc) ); return TRUST_UNKNOWN; } if(opt.force_ownertrust) { log_info("force trust for key %s to %s\n", keystr(kid),trust_value_to_string(opt.force_ownertrust)); update_ownertrust(pk,opt.force_ownertrust); ot=opt.force_ownertrust; } else { ot=edit_ownertrust(pk,0); if(ot>0) ot = get_ownertrust (pk); else if(ot==0) ot = minimum?minimum:TRUST_UNDEFINED; else ot = -1; /* quit */ } free_public_key( pk ); return ot; } static void mark_keyblock_seen (KeyHashTable tbl, KBNODE node) { for ( ;node; node = node->next ) if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { u32 aki[2]; keyid_from_pk (node->pkt->pkt.public_key, aki); add_key_hash_table (tbl, aki); } } static void dump_key_array (int depth, struct key_array *keys) { struct key_array *kar; for (kar=keys; kar->keyblock; kar++) { KBNODE node = kar->keyblock; u32 kid[2]; keyid_from_pk(node->pkt->pkt.public_key, kid); printf ("%d:%08lX%08lX:K::%c::::\n", depth, (ulong)kid[0], (ulong)kid[1], '?'); for (; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { int len = node->pkt->pkt.user_id->len; if (len > 30) len = 30; printf ("%d:%08lX%08lX:U:::%c:::", depth, (ulong)kid[0], (ulong)kid[1], (node->flag & 4)? 'f': (node->flag & 2)? 'm': (node->flag & 1)? 'q':'-'); print_string (stdout, node->pkt->pkt.user_id->name, len, ':'); putchar (':'); putchar ('\n'); } } } } static void store_validation_status (int depth, KBNODE keyblock, KeyHashTable stored) { KBNODE node; int status; int any = 0; for (node=keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; if (node->flag & 4) status = TRUST_FULLY; else if (node->flag & 2) status = TRUST_MARGINAL; else if (node->flag & 1) status = TRUST_UNDEFINED; else status = 0; if (status) { update_validity (keyblock->pkt->pkt.public_key, uid, depth, status); mark_keyblock_seen(stored,keyblock); any = 1; } } } if (any) do_sync (); } /* * check whether the signature sig is in the klist k */ static struct key_item * is_in_klist (struct key_item *k, PKT_signature *sig) { for (; k; k = k->next) { if (k->kid[0] == sig->keyid[0] && k->kid[1] == sig->keyid[1]) return k; } return NULL; } /* * Mark the signature of the given UID which are used to certify it. * To do this, we first revmove all signatures which are not valid and * from the remain ones we look for the latest one. If this is not a * certification revocation signature we mark the signature by setting * node flag bit 8. Revocations are marked with flag 11, and sigs * from unavailable keys are marked with flag 12. Note that flag bits * 9 and 10 are used for internal purposes. */ static void mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode, u32 *main_kid, struct key_item *klist, u32 curtime, u32 *next_expire) { KBNODE node; PKT_signature *sig; /* first check all signatures */ for (node=uidnode->next; node; node = node->next) { int rc; node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); if (node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; /* ready */ if (node->pkt->pkttype != PKT_SIGNATURE) continue; sig = node->pkt->pkt.signature; if (main_kid && sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1]) continue; /* ignore self-signatures if we pass in a main_kid */ if (!IS_UID_SIG(sig) && !IS_UID_REV(sig)) continue; /* we only look at these signature classes */ if(sig->sig_class>=0x11 && sig->sig_class<=0x13 && sig->sig_class-0x10flag |= 1<<12; continue; } node->flag |= 1<<9; } /* reset the remaining flags */ for (; node; node = node->next) node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); /* kbnode flag usage: bit 9 is here set for signatures to consider, * bit 10 will be set by the loop to keep track of keyIDs already * processed, bit 8 will be set for the usable signatures, and bit * 11 will be set for usable revocations. */ /* for each cert figure out the latest valid one */ for (node=uidnode->next; node; node = node->next) { KBNODE n, signode; u32 kid[2]; u32 sigdate; if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; if ( !(node->flag & (1<<9)) ) continue; /* not a node to look at */ if ( (node->flag & (1<<10)) ) continue; /* signature with a keyID already processed */ node->flag |= (1<<10); /* mark this node as processed */ sig = node->pkt->pkt.signature; signode = node; sigdate = sig->timestamp; kid[0] = sig->keyid[0]; kid[1] = sig->keyid[1]; /* Now find the latest and greatest signature */ for (n=uidnode->next; n; n = n->next) { if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; if ( !(n->flag & (1<<9)) ) continue; if ( (n->flag & (1<<10)) ) continue; /* shortcut already processed signatures */ sig = n->pkt->pkt.signature; if (kid[0] != sig->keyid[0] || kid[1] != sig->keyid[1]) continue; n->flag |= (1<<10); /* mark this node as processed */ /* If signode is nonrevocable and unexpired and n isn't, then take signode (skip). It doesn't matter which is older: if signode was older then we don't want to take n as signode is nonrevocable. If n was older then we're automatically fine. */ if(((IS_UID_SIG(signode->pkt->pkt.signature) && !signode->pkt->pkt.signature->flags.revocable && (signode->pkt->pkt.signature->expiredate==0 || signode->pkt->pkt.signature->expiredate>curtime))) && (!(IS_UID_SIG(n->pkt->pkt.signature) && !n->pkt->pkt.signature->flags.revocable && (n->pkt->pkt.signature->expiredate==0 || n->pkt->pkt.signature->expiredate>curtime)))) continue; /* If n is nonrevocable and unexpired and signode isn't, then take n. Again, it doesn't matter which is older: if n was older then we don't want to take signode as n is nonrevocable. If signode was older then we're automatically fine. */ if((!(IS_UID_SIG(signode->pkt->pkt.signature) && !signode->pkt->pkt.signature->flags.revocable && (signode->pkt->pkt.signature->expiredate==0 || signode->pkt->pkt.signature->expiredate>curtime))) && ((IS_UID_SIG(n->pkt->pkt.signature) && !n->pkt->pkt.signature->flags.revocable && (n->pkt->pkt.signature->expiredate==0 || n->pkt->pkt.signature->expiredate>curtime)))) { signode = n; sigdate = sig->timestamp; continue; } /* At this point, if it's newer, it goes in as the only remaining possibilities are signode and n are both either revocable or expired or both nonrevocable and unexpired. If the timestamps are equal take the later ordered packet, presuming that the key packets are hopefully in their original order. */ if (sig->timestamp >= sigdate) { signode = n; sigdate = sig->timestamp; } } sig = signode->pkt->pkt.signature; if (IS_UID_SIG (sig)) { /* this seems to be a usable one which is not revoked. * Just need to check whether there is an expiration time, * We do the expired certification after finding a suitable * certification, the assumption is that a signator does not * want that after the expiration of his certificate the * system falls back to an older certification which has a * different expiration time */ const byte *p; u32 expire; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_EXPIRE, NULL ); - expire = p? sig->timestamp + buffer_to_u32(p) : 0; + expire = p? sig->timestamp + buf32_to_u32 (p) : 0; if (expire==0 || expire > curtime ) { signode->flag |= (1<<8); /* yeah, found a good cert */ if (next_expire && expire && expire < *next_expire) *next_expire = expire; } } else signode->flag |= (1<<11); } } static int clean_sigs_from_uid(KBNODE keyblock,KBNODE uidnode,int noisy,int self_only) { int deleted=0; KBNODE node; u32 keyid[2]; assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY); keyid_from_pk(keyblock->pkt->pkt.public_key,keyid); /* Passing in a 0 for current time here means that we'll never weed out an expired sig. This is correct behavior since we want to keep the most recent expired sig in a series. */ mark_usable_uid_certs(keyblock,uidnode,NULL,NULL,0,NULL); /* What we want to do here is remove signatures that are not considered as part of the trust calculations. Thus, all invalid signatures are out, as are any signatures that aren't the last of a series of uid sigs or revocations It breaks down like this: coming out of mark_usable_uid_certs, if a sig is unflagged, it is not even a candidate. If a sig has flag 9 or 10, that means it was selected as a candidate and vetted. If a sig has flag 8 it is a usable signature. If a sig has flag 11 it is a usable revocation. If a sig has flag 12 it was issued by an unavailable key. "Usable" here means the most recent valid signature/revocation in a series from a particular signer. Delete everything that isn't a usable uid sig (which might be expired), a usable revocation, or a sig from an unavailable key. */ for(node=uidnode->next; node && node->pkt->pkttype==PKT_SIGNATURE; node=node->next) { int keep=self_only?(node->pkt->pkt.signature->keyid[0]==keyid[0] && node->pkt->pkt.signature->keyid[1]==keyid[1]):1; /* Keep usable uid sigs ... */ if((node->flag & (1<<8)) && keep) continue; /* ... and usable revocations... */ if((node->flag & (1<<11)) && keep) continue; /* ... and sigs from unavailable keys. */ /* disabled for now since more people seem to want sigs from unavailable keys removed altogether. */ /* if(node->flag & (1<<12)) continue; */ /* Everything else we delete */ /* At this point, if 12 is set, the signing key was unavailable. If 9 or 10 is set, it's superseded. Otherwise, it's invalid. */ if(noisy) log_info("removing signature from key %s on user ID \"%s\": %s\n", keystr(node->pkt->pkt.signature->keyid), uidnode->pkt->pkt.user_id->name, node->flag&(1<<12)?"key unavailable": node->flag&(1<<9)?"signature superseded":"invalid signature"); delete_kbnode(node); deleted++; } return deleted; } /* This is substantially easier than clean_sigs_from_uid since we just have to establish if the uid has a valid self-sig, is not revoked, and is not expired. Note that this does not take into account whether the uid has a trust path to it - just whether the keyholder themselves has certified the uid. Returns true if the uid was compacted. To "compact" a user ID, we simply remove ALL signatures except the self-sig that caused the user ID to be remove-worthy. We don't actually remove the user ID packet itself since it might be ressurected in a later merge. Note that this function requires that the caller has already done a merge_keys_and_selfsig(). TODO: change the import code to allow importing a uid with only a revocation if the uid already exists on the keyring. */ static int clean_uid_from_key(KBNODE keyblock,KBNODE uidnode,int noisy) { KBNODE node; PKT_user_id *uid=uidnode->pkt->pkt.user_id; int deleted=0; assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY); assert(uidnode->pkt->pkttype==PKT_USER_ID); /* Skip valid user IDs, compacted user IDs, and non-self-signed user IDs if --allow-non-selfsigned-uid is set. */ if(uid->created || uid->flags.compacted || (!uid->is_expired && !uid->is_revoked && opt.allow_non_selfsigned_uid)) return 0; for(node=uidnode->next; node && node->pkt->pkttype==PKT_SIGNATURE; node=node->next) if(!node->pkt->pkt.signature->flags.chosen_selfsig) { delete_kbnode(node); deleted=1; uidnode->pkt->pkt.user_id->flags.compacted=1; } if(noisy) { const char *reason; char *user=utf8_to_native(uid->name,uid->len,0); if(uid->is_revoked) reason=_("revoked"); else if(uid->is_expired) reason=_("expired"); else reason=_("invalid"); log_info("compacting user ID \"%s\" on key %s: %s\n", user,keystr_from_pk(keyblock->pkt->pkt.public_key), reason); xfree(user); } return deleted; } /* Needs to be called after a merge_keys_and_selfsig() */ void clean_one_uid(KBNODE keyblock,KBNODE uidnode,int noisy,int self_only, int *uids_cleaned,int *sigs_cleaned) { int dummy; assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY); assert(uidnode->pkt->pkttype==PKT_USER_ID); if(!uids_cleaned) uids_cleaned=&dummy; if(!sigs_cleaned) sigs_cleaned=&dummy; /* Do clean_uid_from_key first since if it fires off, we don't have to bother with the other */ *uids_cleaned+=clean_uid_from_key(keyblock,uidnode,noisy); if(!uidnode->pkt->pkt.user_id->flags.compacted) *sigs_cleaned+=clean_sigs_from_uid(keyblock,uidnode,noisy,self_only); } void clean_key(KBNODE keyblock,int noisy,int self_only, int *uids_cleaned,int *sigs_cleaned) { KBNODE uidnode; merge_keys_and_selfsig(keyblock); for(uidnode=keyblock->next; uidnode && uidnode->pkt->pkttype!=PKT_PUBLIC_SUBKEY; uidnode=uidnode->next) if(uidnode->pkt->pkttype==PKT_USER_ID) clean_one_uid(keyblock,uidnode,noisy,self_only, uids_cleaned,sigs_cleaned); } /* Returns a sanitized copy of the regexp (which might be "", but not NULL). */ static char * sanitize_regexp(const char *old) { size_t start=0,len=strlen(old),idx=0; int escaped=0,standard_bracket=0; char *new=xmalloc((len*2)+1); /* enough to \-escape everything if we have to */ /* There are basically two commonly-used regexps here. GPG and most versions of PGP use "<[^>]+[@.]example\.com>$" and PGP (9) command line uses "example.com" (i.e. whatever the user specfies, and we can't expect users know to use "\." instead of "."). So here are the rules: we're allowed to start with "<[^>]+[@.]" and end with ">$" or start and end with nothing. In between, the only legal regex character is ".", and everything else gets escaped. Part of the gotcha here is that some regex packages allow more than RFC-4880 requires. For example, 4880 has no "{}" operator, but GNU regex does. Commenting removes these operators from consideration. A possible future enhancement is to use commenting to effectively back off a given regex to the Henry Spencer syntax in 4880. -dshaw */ /* Are we bracketed between "<[^>]+[@.]" and ">$" ? */ if(len>=12 && strncmp(old,"<[^>]+[@.]",10)==0 && old[len-2]=='>' && old[len-1]=='$') { strcpy(new,"<[^>]+[@.]"); idx=strlen(new); standard_bracket=1; start+=10; len-=2; } /* Walk the remaining characters and ensure that everything that is left is not an operational regex character. */ for(;start$", then it was escaping the ">" and is fine. If the regexp actually ended with the bare "\", then it's an illegal regexp and regcomp should kick it out. */ if(standard_bracket) strcat(new,">$"); return new; } /* Used by validate_one_keyblock to confirm a regexp within a trust signature. Returns 1 for match, and 0 for no match or regex error. */ static int check_regexp(const char *expr,const char *string) { #ifdef DISABLE_REGEX /* When DISABLE_REGEX is defined, assume all regexps do not match. */ return 0; #else int ret; char *regexp; regexp=sanitize_regexp(expr); #ifdef __riscos__ ret=riscos_check_regexp(expr, string, DBG_TRUST); #else { regex_t pat; ret=regcomp(&pat,regexp,REG_ICASE|REG_NOSUB|REG_EXTENDED); if(ret==0) { ret=regexec(&pat,string,0,NULL,0); regfree(&pat); ret=(ret==0); } } #endif if(DBG_TRUST) log_debug("regexp `%s' (`%s') on `%s': %s\n", regexp,expr,string,ret==0?"YES":"NO"); xfree(regexp); return ret; #endif } /* * Return true if the key is signed by one of the keys in the given * key ID list. User IDs with a valid signature are marked by node * flags as follows: * flag bit 0: There is at least one signature * 1: There is marginal confidence that this is a legitimate uid * 2: There is full confidence that this is a legitimate uid. * 8: Used for internal purposes. * 9: Ditto (in mark_usable_uid_certs()) * 10: Ditto (ditto) * This function assumes that all kbnode flags are cleared on entry. */ static int validate_one_keyblock (KBNODE kb, struct key_item *klist, u32 curtime, u32 *next_expire) { struct key_item *kr; KBNODE node, uidnode=NULL; PKT_user_id *uid=NULL; PKT_public_key *pk = kb->pkt->pkt.public_key; u32 main_kid[2]; int issigned=0, any_signed = 0; keyid_from_pk(pk, main_kid); for (node=kb; node; node = node->next) { /* A bit of discussion here: is it better for the web of trust to be built among only self-signed uids? On the one hand, a self-signed uid is a statement that the key owner definitely intended that uid to be there, but on the other hand, a signed (but not self-signed) uid does carry trust, of a sort, even if it is a statement being made by people other than the key owner "through" the uids on the key owner's key. I'm going with the latter. However, if the user ID was explicitly revoked, or passively allowed to expire, that should stop validity through the user ID until it is resigned. -dshaw */ if (node->pkt->pkttype == PKT_USER_ID && !node->pkt->pkt.user_id->is_revoked && !node->pkt->pkt.user_id->is_expired) { if (uidnode && issigned) { if (uid->help_full_count >= opt.completes_needed || uid->help_marginal_count >= opt.marginals_needed ) uidnode->flag |= 4; else if (uid->help_full_count || uid->help_marginal_count) uidnode->flag |= 2; uidnode->flag |= 1; any_signed = 1; } uidnode = node; uid=uidnode->pkt->pkt.user_id; /* If the selfsig is going to expire... */ if(uid->expiredate && uid->expiredate<*next_expire) *next_expire = uid->expiredate; issigned = 0; get_validity_counts(pk,uid); mark_usable_uid_certs (kb, uidnode, main_kid, klist, curtime, next_expire); } else if (node->pkt->pkttype == PKT_SIGNATURE && (node->flag & (1<<8)) && uid) { /* Note that we are only seeing unrevoked sigs here */ PKT_signature *sig = node->pkt->pkt.signature; kr = is_in_klist (klist, sig); /* If the trust_regexp does not match, it's as if the sig did not exist. This is safe for non-trust sigs as well since we don't accept a regexp on the sig unless it's a trust sig. */ if (kr && (kr->trust_regexp==NULL || opt.trust_model!=TM_PGP || (uidnode && check_regexp(kr->trust_regexp, uidnode->pkt->pkt.user_id->name)))) { /* Are we part of a trust sig chain? We always favor the latest trust sig, rather than the greater or lesser trust sig or value. I could make a decent argument for any of these cases, but this seems to be what PGP does, and I'd like to be compatible. -dms */ if(opt.trust_model==TM_PGP && sig->trust_depth && pk->trust_timestamp<=sig->timestamp) { byte depth; /* If the depth on the signature is less than the chain currently has, then use the signature depth so we don't increase the depth beyond what the signer wanted. If the depth on the signature is more than the chain currently has, then use the chain depth so we use as much of the signature depth as the chain will permit. An ultimately trusted signature can restart the depth to whatever level it likes. */ if(sig->trust_depthtrust_depth || kr->ownertrust==TRUST_ULTIMATE) depth=sig->trust_depth; else depth=kr->trust_depth; if(depth) { if(DBG_TRUST) log_debug("trust sig on %s, sig depth is %d," " kr depth is %d\n", uidnode->pkt->pkt.user_id->name, sig->trust_depth, kr->trust_depth); /* If we got here, we know that: this is a trust sig. it's a newer trust sig than any previous trust sig on this key (not uid). it is legal in that it was either generated by an ultimate key, or a key that was part of a trust chain, and the depth does not violate the original trust sig. if there is a regexp attached, it matched successfully. */ if(DBG_TRUST) log_debug("replacing trust value %d with %d and " "depth %d with %d\n", pk->trust_value,sig->trust_value, pk->trust_depth,depth); pk->trust_value=sig->trust_value; pk->trust_depth=depth-1; /* If the trust sig contains a regexp, record it on the pk for the next round. */ if(sig->trust_regexp) pk->trust_regexp=sig->trust_regexp; } } if (kr->ownertrust == TRUST_ULTIMATE) uid->help_full_count = opt.completes_needed; else if (kr->ownertrust == TRUST_FULLY) uid->help_full_count++; else if (kr->ownertrust == TRUST_MARGINAL) uid->help_marginal_count++; issigned = 1; } } } if (uidnode && issigned) { if (uid->help_full_count >= opt.completes_needed || uid->help_marginal_count >= opt.marginals_needed ) uidnode->flag |= 4; else if (uid->help_full_count || uid->help_marginal_count) uidnode->flag |= 2; uidnode->flag |= 1; any_signed = 1; } return any_signed; } static int search_skipfnc (void *opaque, u32 *kid, PKT_user_id *dummy) { return test_key_hash_table ((KeyHashTable)opaque, kid); } /* * Scan all keys and return a key_array of all suitable keys from * kllist. The caller has to pass keydb handle so that we don't use * to create our own. Returns either a key_array or NULL in case of * an error. No results found are indicated by an empty array. * Caller hast to release the returned array. */ static struct key_array * validate_key_list (KEYDB_HANDLE hd, KeyHashTable full_trust, struct key_item *klist, u32 curtime, u32 *next_expire) { KBNODE keyblock = NULL; struct key_array *keys = NULL; size_t nkeys, maxkeys; int rc; KEYDB_SEARCH_DESC desc; maxkeys = 1000; keys = xmalloc ((maxkeys+1) * sizeof *keys); nkeys = 0; rc = keydb_search_reset (hd); if (rc) { log_error ("keydb_search_reset failed: %s\n", g10_errstr(rc)); xfree (keys); return NULL; } memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_FIRST; desc.skipfnc = search_skipfnc; desc.skipfncvalue = full_trust; rc = keydb_search (hd, &desc, 1); if (rc == -1) { keys[nkeys].keyblock = NULL; return keys; } if (rc) { log_error ("keydb_search_first failed: %s\n", g10_errstr(rc)); xfree (keys); return NULL; } desc.mode = KEYDB_SEARCH_MODE_NEXT; /* change mode */ do { PKT_public_key *pk; rc = keydb_get_keyblock (hd, &keyblock); if (rc) { log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc)); xfree (keys); return NULL; } if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY) { log_debug ("ooops: invalid pkttype %d encountered\n", keyblock->pkt->pkttype); dump_kbnode (keyblock); release_kbnode(keyblock); continue; } /* prepare the keyblock for further processing */ merge_keys_and_selfsig (keyblock); clear_kbnode_flags (keyblock); pk = keyblock->pkt->pkt.public_key; if (pk->has_expired || pk->is_revoked) { /* it does not make sense to look further at those keys */ mark_keyblock_seen (full_trust, keyblock); } else if (validate_one_keyblock (keyblock, klist, curtime, next_expire)) { KBNODE node; if (pk->expiredate && pk->expiredate >= curtime && pk->expiredate < *next_expire) *next_expire = pk->expiredate; if (nkeys == maxkeys) { maxkeys += 1000; keys = xrealloc (keys, (maxkeys+1) * sizeof *keys); } keys[nkeys++].keyblock = keyblock; /* Optimization - if all uids are fully trusted, then we never need to consider this key as a candidate again. */ for (node=keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && !(node->flag & 4)) break; if(node==NULL) mark_keyblock_seen (full_trust, keyblock); keyblock = NULL; } release_kbnode (keyblock); keyblock = NULL; } while ( !(rc = keydb_search (hd, &desc, 1)) ); if (rc && rc != -1) { log_error ("keydb_search_next failed: %s\n", g10_errstr(rc)); xfree (keys); return NULL; } keys[nkeys].keyblock = NULL; return keys; } /* Caller must sync */ static void reset_trust_records(void) { TRUSTREC rec; ulong recnum; int count = 0, nreset = 0; for (recnum=1; !tdbio_read_record (recnum, &rec, 0); recnum++ ) { if(rec.rectype==RECTYPE_TRUST) { count++; if(rec.r.trust.min_ownertrust) { rec.r.trust.min_ownertrust=0; write_record(&rec); } } else if(rec.rectype==RECTYPE_VALID && ((rec.r.valid.validity&TRUST_MASK) || rec.r.valid.marginal_count || rec.r.valid.full_count)) { rec.r.valid.validity &= ~TRUST_MASK; rec.r.valid.marginal_count=rec.r.valid.full_count=0; nreset++; write_record(&rec); } } if (opt.verbose) log_info (_("%d keys processed (%d validity counts cleared)\n"), count, nreset); } /* * Run the key validation procedure. * * This works this way: * Step 1: Find all ultimately trusted keys (UTK). * mark them all as seen and put them into klist. * Step 2: loop max_cert_times * Step 3: if OWNERTRUST of any key in klist is undefined * ask user to assign ownertrust * Step 4: Loop over all keys in the keyDB which are not marked seen * Step 5: if key is revoked or expired * mark key as seen * continue loop at Step 4 * Step 6: For each user ID of that key signed by a key in klist * Calculate validity by counting trusted signatures. * Set validity of user ID * Step 7: If any signed user ID was found * mark key as seen * End Loop * Step 8: Build a new klist from all fully trusted keys from step 6 * End Loop * Ready * */ static int validate_keys (int interactive) { int rc = 0; int quit=0; struct key_item *klist = NULL; struct key_item *k; struct key_array *keys = NULL; struct key_array *kar; KEYDB_HANDLE kdb = NULL; KBNODE node; int depth; int ot_unknown, ot_undefined, ot_never, ot_marginal, ot_full, ot_ultimate; KeyHashTable stored,used,full_trust; u32 start_time, next_expire; /* Make sure we have all sigs cached. TODO: This is going to require some architectual re-thinking, as it is agonizingly slow. Perhaps combine this with reset_trust_records(), or only check the caches on keys that are actually involved in the web of trust. */ keydb_rebuild_caches(0); start_time = make_timestamp (); next_expire = 0xffffffff; /* set next expire to the year 2106 */ stored = new_key_hash_table (); used = new_key_hash_table (); full_trust = new_key_hash_table (); kdb = keydb_new (0); reset_trust_records(); /* Fixme: Instead of always building a UTK list, we could just build it * here when needed */ if (!utk_list) { if (!opt.quiet) log_info (_("no ultimately trusted keys found\n")); goto leave; } /* mark all UTKs as used and fully_trusted and set validity to ultimate */ for (k=utk_list; k; k = k->next) { KBNODE keyblock; PKT_public_key *pk; keyblock = get_pubkeyblock (k->kid); if (!keyblock) { log_error (_("public key of ultimately" " trusted key %s not found\n"), keystr(k->kid)); continue; } mark_keyblock_seen (used, keyblock); mark_keyblock_seen (stored, keyblock); mark_keyblock_seen (full_trust, keyblock); pk = keyblock->pkt->pkt.public_key; for (node=keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) update_validity (pk, node->pkt->pkt.user_id, 0, TRUST_ULTIMATE); } if ( pk->expiredate && pk->expiredate >= start_time && pk->expiredate < next_expire) next_expire = pk->expiredate; release_kbnode (keyblock); do_sync (); } klist = utk_list; log_info(_("%d marginal(s) needed, %d complete(s) needed, %s trust model\n"), opt.marginals_needed,opt.completes_needed,trust_model_string()); for (depth=0; depth < opt.max_cert_depth; depth++) { int valids=0,key_count; /* See whether we should assign ownertrust values to the keys in klist. */ ot_unknown = ot_undefined = ot_never = 0; ot_marginal = ot_full = ot_ultimate = 0; for (k=klist; k; k = k->next) { int min=0; /* 120 and 60 are as per RFC2440 */ if(k->trust_value>=120) min=TRUST_FULLY; else if(k->trust_value>=60) min=TRUST_MARGINAL; if(min!=k->min_ownertrust) update_min_ownertrust(k->kid,min); if (interactive && k->ownertrust == TRUST_UNKNOWN) { k->ownertrust = ask_ownertrust (k->kid,min); if (k->ownertrust == (unsigned int)(-1)) { quit=1; goto leave; } } /* This can happen during transition from an old trustdb before trust sigs. It can also happen if a user uses two different versions of GnuPG or changes the --trust-model setting. */ if(k->ownertrustkid[0],(ulong)k->kid[1], trust_value_to_string(k->ownertrust), trust_value_to_string(min)); k->ownertrust=min; } if (k->ownertrust == TRUST_UNKNOWN) ot_unknown++; else if (k->ownertrust == TRUST_UNDEFINED) ot_undefined++; else if (k->ownertrust == TRUST_NEVER) ot_never++; else if (k->ownertrust == TRUST_MARGINAL) ot_marginal++; else if (k->ownertrust == TRUST_FULLY) ot_full++; else if (k->ownertrust == TRUST_ULTIMATE) ot_ultimate++; valids++; } /* Find all keys which are signed by a key in kdlist */ keys = validate_key_list (kdb, full_trust, klist, start_time, &next_expire); if (!keys) { log_error ("validate_key_list failed\n"); rc = G10ERR_GENERAL; goto leave; } for (key_count=0, kar=keys; kar->keyblock; kar++, key_count++) ; /* Store the calculated valididation status somewhere */ if (opt.verbose > 1) dump_key_array (depth, keys); for (kar=keys; kar->keyblock; kar++) store_validation_status (depth, kar->keyblock, stored); log_info (_("depth: %d valid: %3d signed: %3d" " trust: %d-, %dq, %dn, %dm, %df, %du\n"), depth, valids, key_count, ot_unknown, ot_undefined, ot_never, ot_marginal, ot_full, ot_ultimate ); /* Build a new kdlist from all fully valid keys in KEYS */ if (klist != utk_list) release_key_items (klist); klist = NULL; for (kar=keys; kar->keyblock; kar++) { for (node=kar->keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && (node->flag & 4)) { u32 kid[2]; /* have we used this key already? */ keyid_from_pk (kar->keyblock->pkt->pkt.public_key, kid); if(test_key_hash_table(used,kid)==0) { /* Normally we add both the primary and subkey ids to the hash via mark_keyblock_seen, but since we aren't using this hash as a skipfnc, that doesn't matter here. */ add_key_hash_table (used,kid); k = new_key_item (); k->kid[0]=kid[0]; k->kid[1]=kid[1]; k->ownertrust = (get_ownertrust (kar->keyblock->pkt->pkt.public_key) & TRUST_MASK); k->min_ownertrust = get_min_ownertrust(kar->keyblock->pkt->pkt.public_key); k->trust_depth= kar->keyblock->pkt->pkt.public_key->trust_depth; k->trust_value= kar->keyblock->pkt->pkt.public_key->trust_value; if(kar->keyblock->pkt->pkt.public_key->trust_regexp) k->trust_regexp= xstrdup(kar->keyblock->pkt-> pkt.public_key->trust_regexp); k->next = klist; klist = k; break; } } } } release_key_array (keys); keys = NULL; if (!klist) break; /* no need to dive in deeper */ } leave: keydb_release (kdb); release_key_array (keys); release_key_items (klist); release_key_hash_table (full_trust); release_key_hash_table (used); release_key_hash_table (stored); if (!rc && !quit) /* mark trustDB as checked */ { if (next_expire == 0xffffffff || next_expire < start_time ) tdbio_write_nextcheck (0); else { tdbio_write_nextcheck (next_expire); log_info (_("next trustdb check due at %s\n"), strtimestamp (next_expire)); } if(tdbio_update_version_record()!=0) { log_error(_("unable to update trustdb version record: " "write failed: %s\n"), g10_errstr(rc)); tdbio_invalid(); } do_sync (); pending_check_trustdb = 0; } return rc; } diff --git a/include/host2net.h b/include/host2net.h index fe0ec41fd..ecb00dce4 100644 --- a/include/host2net.h +++ b/include/host2net.h @@ -1,42 +1,102 @@ -/* host2net.h - Some macros - * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. +/* host2net.h - Endian conversion macros + * Copyright (C) 1998, 2014, 2015 Werner Koch * * This file is part of GNUPG. * * GNUPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GNUPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ -#ifndef G10_HOST2NET_H -#define G10_HOST2NET_H +#ifndef GNUPG_COMMON_HOST2NET_H +#define GNUPG_COMMON_HOST2NET_H #include "types.h" -#define buftoulong( p ) ((*(byte*)(p) << 24) | (*((byte*)(p)+1)<< 16) | \ - (*((byte*)(p)+2) << 8) | (*((byte*)(p)+3))) -#define buftoushort( p ) ((*((byte*)(p)) << 8) | (*((byte*)(p)+1))) #define ulongtobuf( p, a ) do { \ ((byte*)p)[0] = a >> 24; \ ((byte*)p)[1] = a >> 16; \ ((byte*)p)[2] = a >> 8; \ ((byte*)p)[3] = a ; \ } while(0) #define ushorttobuf( p, a ) do { \ ((byte*)p)[0] = a >> 8; \ ((byte*)p)[1] = a ; \ } while(0) -#define buftou32( p) buftoulong( (p) ) -#define u32tobuf( p, a) ulongtobuf( (p), (a) ) -#endif /*G10_HOST2NET_H*/ +static inline unsigned long +buf16_to_ulong (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned long)p[0] << 8) | p[1]); +} + +static inline unsigned int +buf16_to_uint (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned int)p[0] << 8) | p[1]); +} + +static inline unsigned short +buf16_to_ushort (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned short)p[0] << 8) | p[1]); +} + +static inline u16 +buf16_to_u16 (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((u16)p[0] << 8) | p[1]); +} + +static inline size_t +buf32_to_size_t (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((size_t)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static inline unsigned long +buf32_to_ulong (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned long)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static inline unsigned int +buf32_to_uint (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned int)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static inline u32 +buf32_to_u32 (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((u32)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + + +#endif /*GNUPG_COMMON_HOST2NET_H*/