diff --git a/g10/seckey-cert.c b/g10/seckey-cert.c index 61cc2ea87..a7a873986 100644 --- a/g10/seckey-cert.c +++ b/g10/seckey-cert.c @@ -1,255 +1,255 @@ /* seckey-cert.c - Not anymore used * Copyright (C) 1998, 1999, 2000, 2001, 2002, * 2006, 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 . */ #error Not anymore used - only kept for reference in the repository. #include #include #include #include #include "gpg.h" -#include "util.h" +#include "../common/util.h" #include "packet.h" #include "keydb.h" #include "cipher.h" #include "main.h" #include "options.h" -#include "i18n.h" -#include "status.h" +#include "../common/i18n.h" +#include "../common/status.h" #include "pkglue.h" static int xxxx_do_check( PKT_secret_key *sk, const char *tryagain_text, int mode, int *canceled ) { gpg_error_t err; byte *buffer; u16 csum=0; int i, res; size_t nbytes; if( sk->is_protected ) { /* remove the protection */ DEK *dek = NULL; u32 keyid[4]; /* 4! because we need two of them */ gcry_cipher_hd_t cipher_hd=NULL; PKT_secret_key *save_sk; if( sk->protect.s2k.mode == 1001 ) { log_info(_("secret key parts are not available\n")); return GPG_ERR_UNUSABLE_SECKEY; } if( sk->protect.algo == CIPHER_ALGO_NONE ) BUG(); if( openpgp_cipher_test_algo( sk->protect.algo ) ) { log_info(_("protection algorithm %d%s is not supported\n"), sk->protect.algo,sk->protect.algo==1?" (IDEA)":"" ); return GPG_ERR_CIPHER_ALGO; } if(gcry_md_test_algo (sk->protect.s2k.hash_algo)) { log_info(_("protection digest %d is not supported\n"), sk->protect.s2k.hash_algo); return GPG_ERR_DIGEST_ALGO; } keyid_from_sk( sk, keyid ); keyid[2] = keyid[3] = 0; if (!sk->flags.primary) { keyid[2] = sk->main_keyid[0]; keyid[3] = sk->main_keyid[1]; } dek = passphrase_to_dek( keyid, sk->pubkey_algo, sk->protect.algo, &sk->protect.s2k, mode, tryagain_text, canceled ); if (!dek && canceled && *canceled) return GPG_ERR_CANCELED; err = openpgp_cipher_open (&cipher_hd, sk->protect.algo, GCRY_CIPHER_MODE_CFB, (GCRY_CIPHER_SECURE | (sk->protect.algo >= 100 ? 0 : GCRY_CIPHER_ENABLE_SYNC))); if (err) log_fatal ("cipher open failed: %s\n", gpg_strerror (err) ); err = gcry_cipher_setkey (cipher_hd, dek->key, dek->keylen); if (err) log_fatal ("set key failed: %s\n", gpg_strerror (err) ); xfree(dek); save_sk = copy_secret_key( NULL, sk ); gcry_cipher_setiv ( cipher_hd, sk->protect.iv, sk->protect.ivlen ); csum = 0; if( sk->version >= 4 ) { int ndata; unsigned int ndatabits; byte *p, *data; u16 csumc = 0; i = pubkey_get_npkey(sk->pubkey_algo); assert ( gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE )); p = gcry_mpi_get_opaque ( sk->skey[i], &ndatabits ); ndata = (ndatabits+7)/8; if ( ndata > 1 ) csumc = buf16_to_u16 (p+ndata-2); data = xmalloc_secure ( ndata ); gcry_cipher_decrypt ( cipher_hd, data, ndata, p, ndata ); gcry_mpi_release (sk->skey[i]); sk->skey[i] = NULL ; p = data; if (sk->protect.sha1chk) { /* This is the new SHA1 checksum method to detect tampering with the key as used by the Klima/Rosa attack */ sk->csum = 0; csum = 1; if( ndata < 20 ) log_error("not enough bytes for SHA-1 checksum\n"); else { gcry_md_hd_t h; if ( gcry_md_open (&h, DIGEST_ALGO_SHA1, 1)) BUG(); /* Algo not available. */ gcry_md_write (h, data, ndata - 20); gcry_md_final (h); if (!memcmp (gcry_md_read (h, DIGEST_ALGO_SHA1), data + ndata - 20, 20) ) { /* Digest does match. We have to keep the old style checksum in sk->csum, so that the test used for unprotected keys does work. This test gets used when we are adding new keys. */ sk->csum = csum = checksum (data, ndata-20); } gcry_md_close (h); } } else { if( ndata < 2 ) { log_error("not enough bytes for checksum\n"); sk->csum = 0; csum = 1; } else { csum = checksum( data, ndata-2); sk->csum = data[ndata-2] << 8 | data[ndata-1]; if ( sk->csum != csum ) { /* This is a PGP 7.0.0 workaround */ sk->csum = csumc; /* take the encrypted one */ } } } /* Must check it here otherwise the mpi_read_xx would fail because the length may have an arbitrary value */ if( sk->csum == csum ) { for( ; i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { if ( gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP, p, ndata, &nbytes)) { /* Checksum was okay, but not correctly decrypted. */ sk->csum = 0; csum = 1; break; } ndata -= nbytes; p += nbytes; } /* Note: at this point ndata should be 2 for a simple checksum or 20 for the sha1 digest */ } xfree(data); } else { for(i=pubkey_get_npkey(sk->pubkey_algo); i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { byte *p; size_t ndata; unsigned int ndatabits; assert (gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE)); p = gcry_mpi_get_opaque (sk->skey[i], &ndatabits); ndata = (ndatabits+7)/8; assert (ndata >= 2); assert (ndata == ((p[0] << 8 | p[1]) + 7)/8 + 2); buffer = xmalloc_secure (ndata); gcry_cipher_sync (cipher_hd); buffer[0] = p[0]; buffer[1] = p[1]; gcry_cipher_decrypt (cipher_hd, buffer+2, ndata-2, p+2, ndata-2); csum += checksum (buffer, ndata); gcry_mpi_release (sk->skey[i]); err = gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP, buffer, ndata, &ndata ); xfree (buffer); if (err) { /* Checksum was okay, but not correctly decrypted. */ sk->csum = 0; csum = 1; break; } /* csum += checksum_mpi (sk->skey[i]); */ } } gcry_cipher_close ( cipher_hd ); /* Now let's see whether we have used the correct passphrase. */ if( csum != sk->csum ) { copy_secret_key( sk, save_sk ); passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo ); free_secret_key( save_sk ); return gpg_error (GPG_ERR_BAD_PASSPHRASE); } /* The checksum may fail, so we also check the key itself. */ res = pk_check_secret_key ( sk->pubkey_algo, sk->skey ); if( res ) { copy_secret_key( sk, save_sk ); passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo ); free_secret_key( save_sk ); return gpg_error (GPG_ERR_BAD_PASSPHRASE); } free_secret_key( save_sk ); sk->is_protected = 0; } else { /* not protected, assume it is okay if the checksum is okay */ csum = 0; for(i=pubkey_get_npkey(sk->pubkey_algo); i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { csum += checksum_mpi( sk->skey[i] ); } if( csum != sk->csum ) return GPG_ERR_CHECKSUM; } return 0; } diff --git a/scd/iso7816.c b/scd/iso7816.c index 8f796981f..d146bd00a 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -1,814 +1,814 @@ /* iso7816.c - ISO 7816 commands * 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 . */ #include #include #include #include #include #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 "../common/util.h" +#include "../common/i18n.h" #else /* GNUPG_MAJOR_VERSION != 1 */ #include "scdaemon.h" #endif /* GNUPG_MAJOR_VERSION != 1 */ #include "iso7816.h" #include "apdu.h" #define CMD_SELECT_FILE 0xA4 #define CMD_VERIFY ISO7816_VERIFY #define CMD_CHANGE_REFERENCE_DATA ISO7816_CHANGE_REFERENCE_DATA #define CMD_RESET_RETRY_COUNTER ISO7816_RESET_RETRY_COUNTER #define CMD_GET_DATA 0xCA #define CMD_PUT_DATA 0xDA #define CMD_MSE 0x22 #define CMD_PSO 0x2A #define CMD_INTERNAL_AUTHENTICATE 0x88 #define CMD_GENERATE_KEYPAIR 0x47 #define CMD_GET_CHALLENGE 0x84 #define CMD_READ_BINARY 0xB0 #define CMD_READ_RECORD 0xB2 static gpg_error_t map_sw (int sw) { gpg_err_code_t ec; switch (sw) { case SW_EEPROM_FAILURE: ec = GPG_ERR_HARDWARE; break; case SW_TERM_STATE: ec = GPG_ERR_OBJ_TERM_STATE; break; case SW_WRONG_LENGTH: ec = GPG_ERR_INV_VALUE; break; case SW_SM_NOT_SUP: ec = GPG_ERR_NOT_SUPPORTED; break; case SW_CC_NOT_SUP: ec = GPG_ERR_NOT_SUPPORTED; break; case SW_CHV_WRONG: ec = GPG_ERR_BAD_PIN; break; case SW_CHV_BLOCKED: ec = GPG_ERR_PIN_BLOCKED; break; case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break; case SW_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break; case SW_BAD_PARAMETER: ec = GPG_ERR_INV_VALUE; break; case SW_FILE_NOT_FOUND: ec = GPG_ERR_ENOENT; break; case SW_RECORD_NOT_FOUND:ec= GPG_ERR_NOT_FOUND; break; case SW_REF_NOT_FOUND: ec = GPG_ERR_NO_OBJ; break; case SW_BAD_P0_P1: ec = GPG_ERR_INV_VALUE; break; case SW_EXACT_LENGTH: ec = GPG_ERR_INV_VALUE; break; case SW_INS_NOT_SUP: ec = GPG_ERR_CARD; break; case SW_CLA_NOT_SUP: ec = GPG_ERR_CARD; break; case SW_SUCCESS: ec = 0; break; case SW_HOST_OUT_OF_CORE: ec = GPG_ERR_ENOMEM; break; case SW_HOST_INV_VALUE: ec = GPG_ERR_INV_VALUE; break; case SW_HOST_INCOMPLETE_CARD_RESPONSE: ec = GPG_ERR_CARD; break; case SW_HOST_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break; case SW_HOST_LOCKING_FAILED: ec = GPG_ERR_BUG; break; case SW_HOST_BUSY: ec = GPG_ERR_EBUSY; break; case SW_HOST_NO_CARD: ec = GPG_ERR_CARD_NOT_PRESENT; break; case SW_HOST_CARD_INACTIVE: ec = GPG_ERR_CARD_RESET; break; case SW_HOST_CARD_IO_ERROR: ec = GPG_ERR_EIO; break; case SW_HOST_GENERAL_ERROR: ec = GPG_ERR_GENERAL; break; case SW_HOST_NO_READER: ec = GPG_ERR_ENODEV; break; case SW_HOST_ABORTED: ec = GPG_ERR_CANCELED; break; case SW_HOST_NO_PINPAD: ec = GPG_ERR_NOT_SUPPORTED; break; default: if ((sw & 0x010000)) ec = GPG_ERR_GENERAL; /* Should not happen. */ else if ((sw & 0xff00) == SW_MORE_DATA) ec = 0; /* This should actually never been seen here. */ else ec = GPG_ERR_CARD; } return gpg_error (ec); } /* Map a status word from the APDU layer to a gpg-error code. */ gpg_error_t iso7816_map_sw (int sw) { /* All APDU functions should return 0x9000 on success but for historical reasons of the implementation some return 0 to indicate success. We allow for that here. */ return sw? map_sw (sw) : 0; } /* This function is specialized version of the SELECT FILE command. SLOT is the card and reader as created for example by apdu_open_reader (), AID is a buffer of size AIDLEN holding the requested application ID. The function can't be used to enumerate AIDs and won't return the AID on success. The return value is 0 for okay or a GPG error code. Note that ISO error codes are internally mapped. Bit 0 of FLAGS should be set if the card does not understand P2=0xC0. */ gpg_error_t iso7816_select_application (int slot, const char *aid, size_t aidlen, unsigned int flags) { int sw; sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE, 4, (flags&1)? 0 :0x0c, aidlen, aid); return map_sw (sw); } gpg_error_t iso7816_select_file (int slot, int tag, int is_dir) { int sw, p0, p1; unsigned char tagbuf[2]; tagbuf[0] = (tag >> 8) & 0xff; tagbuf[1] = tag & 0xff; p0 = (tag == 0x3F00)? 0: is_dir? 1:2; p1 = 0x0c; /* No FC return. */ sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE, p0, p1, 2, (char*)tagbuf ); return map_sw (sw); } /* Do a select file command with a direct path. */ gpg_error_t iso7816_select_path (int slot, const unsigned short *path, size_t pathlen) { int sw, p0, p1; unsigned char buffer[100]; int buflen; if (pathlen/2 >= sizeof buffer) return gpg_error (GPG_ERR_TOO_LARGE); for (buflen = 0; pathlen; pathlen--, path++) { buffer[buflen++] = (*path >> 8); buffer[buflen++] = *path; } p0 = 0x08; p1 = 0x0c; /* No FC return. */ sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE, p0, p1, buflen, (char*)buffer ); return map_sw (sw); } /* This is a private command currently only working for TCOS cards. */ gpg_error_t iso7816_list_directory (int slot, int list_dirs, unsigned char **result, size_t *resultlen) { int sw; if (!result || !resultlen) return gpg_error (GPG_ERR_INV_VALUE); *result = NULL; *resultlen = 0; sw = apdu_send (slot, 0, 0x80, 0xAA, list_dirs? 1:2, 0, -1, NULL, result, resultlen); if (sw != SW_SUCCESS) { /* Make sure that pending buffers are released. */ xfree (*result); *result = NULL; *resultlen = 0; } return map_sw (sw); } /* This funcion sends an already formatted APDU to the card. With HANDLE_MORE set to true a MORE DATA status will be handled internally. The return value is a gpg error code (i.e. a mapped status word). This is basically the same as apdu_send_direct but it maps the status word and does not return it in the result buffer. */ gpg_error_t iso7816_apdu_direct (int slot, const void *apdudata, size_t apdudatalen, int handle_more, unsigned char **result, size_t *resultlen) { int sw; if (!result || !resultlen) return gpg_error (GPG_ERR_INV_VALUE); *result = NULL; *resultlen = 0; sw = apdu_send_direct (slot, 0, apdudata, apdudatalen, handle_more, result, resultlen); if (!sw) { if (*resultlen < 2) sw = SW_HOST_GENERAL_ERROR; else { sw = ((*result)[*resultlen-2] << 8) | (*result)[*resultlen-1]; (*resultlen)--; (*resultlen)--; } } if (sw != SW_SUCCESS) { /* Make sure that pending buffers are released. */ xfree (*result); *result = NULL; *resultlen = 0; } return map_sw (sw); } /* Check whether the reader supports the ISO command code COMMAND on the pinpad. Returns 0 on success. */ gpg_error_t iso7816_check_pinpad (int slot, int command, pininfo_t *pininfo) { int sw; sw = apdu_check_pinpad (slot, command, pininfo); return iso7816_map_sw (sw); } /* Perform a VERIFY command on SLOT using the card holder verification vector CHVNO. With PININFO non-NULL the pinpad of the reader will be used. Returns 0 on success. */ gpg_error_t iso7816_verify_kp (int slot, int chvno, pininfo_t *pininfo) { int sw; sw = apdu_pinpad_verify (slot, 0x00, CMD_VERIFY, 0, chvno, pininfo); return map_sw (sw); } /* Perform a VERIFY command on SLOT using the card holder verification vector CHVNO with a CHV of length CHVLEN. Returns 0 on success. */ gpg_error_t iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen) { int sw; sw = apdu_send_simple (slot, 0, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv); return map_sw (sw); } /* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder verification vector CHVNO. With PININFO non-NULL the pinpad of the reader will be used. If IS_EXCHANGE is 0, a "change reference data" is done, otherwise an "exchange reference data". */ gpg_error_t iso7816_change_reference_data_kp (int slot, int chvno, int is_exchange, pininfo_t *pininfo) { int sw; sw = apdu_pinpad_modify (slot, 0x00, CMD_CHANGE_REFERENCE_DATA, is_exchange ? 1 : 0, chvno, pininfo); return map_sw (sw); } /* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder verification vector CHVNO. If the OLDCHV is NULL (and OLDCHVLEN 0), a "change reference data" is done, otherwise an "exchange reference data". The new reference data is expected in NEWCHV of length NEWCHVLEN. */ gpg_error_t iso7816_change_reference_data (int slot, int chvno, const char *oldchv, size_t oldchvlen, const char *newchv, size_t newchvlen) { int sw; char *buf; if ((!oldchv && oldchvlen) || (oldchv && !oldchvlen) || !newchv || !newchvlen ) return gpg_error (GPG_ERR_INV_VALUE); buf = xtrymalloc (oldchvlen + newchvlen); if (!buf) return gpg_error (gpg_err_code_from_errno (errno)); if (oldchvlen) memcpy (buf, oldchv, oldchvlen); memcpy (buf+oldchvlen, newchv, newchvlen); sw = apdu_send_simple (slot, 0, 0x00, CMD_CHANGE_REFERENCE_DATA, oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf); xfree (buf); return map_sw (sw); } gpg_error_t iso7816_reset_retry_counter_with_rc (int slot, int chvno, const char *data, size_t datalen) { int sw; if (!data || !datalen ) return gpg_error (GPG_ERR_INV_VALUE); sw = apdu_send_simple (slot, 0, 0x00, CMD_RESET_RETRY_COUNTER, 0, chvno, datalen, data); return map_sw (sw); } gpg_error_t iso7816_reset_retry_counter (int slot, int chvno, const char *newchv, size_t newchvlen) { int sw; sw = apdu_send_simple (slot, 0, 0x00, CMD_RESET_RETRY_COUNTER, 2, chvno, newchvlen, newchv); return map_sw (sw); } /* Perform a GET DATA command requesting TAG and storing the result in a newly allocated buffer at the address passed by RESULT. Return the length of this data at the address of RESULTLEN. */ gpg_error_t iso7816_get_data (int slot, int extended_mode, int tag, unsigned char **result, size_t *resultlen) { int sw; int le; if (!result || !resultlen) return gpg_error (GPG_ERR_INV_VALUE); *result = NULL; *resultlen = 0; if (extended_mode > 0 && extended_mode < 256) le = 65534; /* Not 65535 in case it is used as some special flag. */ else if (extended_mode > 0) le = extended_mode; else le = 256; sw = apdu_send_le (slot, extended_mode, 0x00, CMD_GET_DATA, ((tag >> 8) & 0xff), (tag & 0xff), -1, NULL, le, result, resultlen); if (sw != SW_SUCCESS) { /* Make sure that pending buffers are released. */ xfree (*result); *result = NULL; *resultlen = 0; return map_sw (sw); } return 0; } /* Perform a PUT DATA command on card in SLOT. Write DATA of length DATALEN to TAG. EXTENDED_MODE controls whether extended length headers or command chaining is used instead of single length bytes. */ gpg_error_t iso7816_put_data (int slot, int extended_mode, int tag, const void *data, size_t datalen) { int sw; sw = apdu_send_simple (slot, extended_mode, 0x00, CMD_PUT_DATA, ((tag >> 8) & 0xff), (tag & 0xff), datalen, (const char*)data); return map_sw (sw); } /* Same as iso7816_put_data but uses an odd instruction byte. */ gpg_error_t iso7816_put_data_odd (int slot, int extended_mode, int tag, const void *data, size_t datalen) { int sw; sw = apdu_send_simple (slot, extended_mode, 0x00, CMD_PUT_DATA+1, ((tag >> 8) & 0xff), (tag & 0xff), datalen, (const char*)data); return map_sw (sw); } /* Manage Security Environment. This is a weird operation and there is no easy abstraction for it. Furthermore, some card seem to have a different interpreation of 7816-8 and thus we resort to let the caller decide what to do. */ gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2, const unsigned char *data, size_t datalen) { int sw; if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 ) return gpg_error (GPG_ERR_INV_VALUE); sw = apdu_send_simple (slot, 0, 0x00, CMD_MSE, p1, p2, data? datalen : -1, (const char*)data); return map_sw (sw); } /* Perform the security operation COMPUTE DIGITAL SIGANTURE. On success 0 is returned and the data is availavle in a newly allocated buffer stored at RESULT with its length stored at RESULTLEN. For LE see do_generate_keypair. */ gpg_error_t iso7816_compute_ds (int slot, int extended_mode, const unsigned char *data, size_t datalen, int le, unsigned char **result, size_t *resultlen) { int sw; if (!data || !datalen || !result || !resultlen) return gpg_error (GPG_ERR_INV_VALUE); *result = NULL; *resultlen = 0; if (!extended_mode) le = 256; /* Ignore provided Le and use what apdu_send uses. */ else if (le >= 0 && le < 256) le = 256; sw = apdu_send_le (slot, extended_mode, 0x00, CMD_PSO, 0x9E, 0x9A, datalen, (const char*)data, le, result, resultlen); if (sw != SW_SUCCESS) { /* Make sure that pending buffers are released. */ xfree (*result); *result = NULL; *resultlen = 0; return map_sw (sw); } return 0; } /* Perform the security operation DECIPHER. PADIND is the padding indicator to be used. It should be 0 if no padding is required, a value of -1 suppresses the padding byte. On success 0 is returned and the plaintext is available in a newly allocated buffer stored at RESULT with its length stored at RESULTLEN. For LE see do_generate_keypair. */ gpg_error_t iso7816_decipher (int slot, int extended_mode, const unsigned char *data, size_t datalen, int le, int padind, unsigned char **result, size_t *resultlen) { int sw; unsigned char *buf; if (!data || !datalen || !result || !resultlen) return gpg_error (GPG_ERR_INV_VALUE); *result = NULL; *resultlen = 0; if (!extended_mode) le = 256; /* Ignore provided Le and use what apdu_send uses. */ else if (le >= 0 && le < 256) le = 256; if (padind >= 0) { /* We need to prepend the padding indicator. */ buf = xtrymalloc (datalen + 1); if (!buf) return gpg_error (gpg_err_code_from_errno (errno)); *buf = padind; /* Padding indicator. */ memcpy (buf+1, data, datalen); sw = apdu_send_le (slot, extended_mode, 0x00, CMD_PSO, 0x80, 0x86, datalen+1, (char*)buf, le, result, resultlen); xfree (buf); } else { sw = apdu_send_le (slot, extended_mode, 0x00, CMD_PSO, 0x80, 0x86, datalen, (const char *)data, le, result, resultlen); } if (sw != SW_SUCCESS) { /* Make sure that pending buffers are released. */ xfree (*result); *result = NULL; *resultlen = 0; return map_sw (sw); } return 0; } /* For LE see do_generate_keypair. */ gpg_error_t iso7816_internal_authenticate (int slot, int extended_mode, const unsigned char *data, size_t datalen, int le, unsigned char **result, size_t *resultlen) { int sw; if (!data || !datalen || !result || !resultlen) return gpg_error (GPG_ERR_INV_VALUE); *result = NULL; *resultlen = 0; if (!extended_mode) le = 256; /* Ignore provided Le and use what apdu_send uses. */ else if (le >= 0 && le < 256) le = 256; sw = apdu_send_le (slot, extended_mode, 0x00, CMD_INTERNAL_AUTHENTICATE, 0, 0, datalen, (const char*)data, le, result, resultlen); if (sw != SW_SUCCESS) { /* Make sure that pending buffers are released. */ xfree (*result); *result = NULL; *resultlen = 0; return map_sw (sw); } return 0; } /* LE is the expected return length. This is usually 0 except if extended length mode is used and more than 256 byte will be returned. In that case a value of -1 uses a large default (e.g. 4096 bytes), a value larger 256 used that value. */ static gpg_error_t do_generate_keypair (int slot, int extended_mode, int read_only, const char *data, size_t datalen, int le, unsigned char **result, size_t *resultlen) { int sw; if (!data || !datalen || !result || !resultlen) return gpg_error (GPG_ERR_INV_VALUE); *result = NULL; *resultlen = 0; sw = apdu_send_le (slot, extended_mode, 0x00, CMD_GENERATE_KEYPAIR, read_only? 0x81:0x80, 0, datalen, data, le >= 0 && le < 256? 256:le, result, resultlen); if (sw != SW_SUCCESS) { /* Make sure that pending buffers are released. */ xfree (*result); *result = NULL; *resultlen = 0; return map_sw (sw); } return 0; } gpg_error_t iso7816_generate_keypair (int slot, int extended_mode, const char *data, size_t datalen, int le, unsigned char **result, size_t *resultlen) { return do_generate_keypair (slot, extended_mode, 0, data, datalen, le, result, resultlen); } gpg_error_t iso7816_read_public_key (int slot, int extended_mode, const char *data, size_t datalen, int le, unsigned char **result, size_t *resultlen) { return do_generate_keypair (slot, extended_mode, 1, data, datalen, le, result, resultlen); } gpg_error_t iso7816_get_challenge (int slot, int length, unsigned char *buffer) { int sw; unsigned char *result; size_t resultlen, n; if (!buffer || length < 1) return gpg_error (GPG_ERR_INV_VALUE); do { result = NULL; n = length > 254? 254 : length; sw = apdu_send_le (slot, 0, 0x00, CMD_GET_CHALLENGE, 0, 0, -1, NULL, n, &result, &resultlen); if (sw != SW_SUCCESS) { /* Make sure that pending buffers are released. */ xfree (result); return map_sw (sw); } if (resultlen > n) resultlen = n; memcpy (buffer, result, resultlen); buffer += resultlen; length -= resultlen; xfree (result); } while (length > 0); return 0; } /* Perform a READ BINARY command requesting a maximum of NMAX bytes from OFFSET. With NMAX = 0 the entire file is read. The result is stored in a newly allocated buffer at the address passed by RESULT. Returns the length of this data at the address of RESULTLEN. */ gpg_error_t iso7816_read_binary (int slot, size_t offset, size_t nmax, unsigned char **result, size_t *resultlen) { int sw; unsigned char *buffer; size_t bufferlen; int read_all = !nmax; size_t n; if (!result || !resultlen) return gpg_error (GPG_ERR_INV_VALUE); *result = NULL; *resultlen = 0; /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus we check for this limit. */ if (offset > 32767) return gpg_error (GPG_ERR_INV_VALUE); do { buffer = NULL; bufferlen = 0; n = read_all? 0 : nmax; sw = apdu_send_le (slot, 0, 0x00, CMD_READ_BINARY, ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL, n, &buffer, &bufferlen); if ( SW_EXACT_LENGTH_P(sw) ) { n = (sw & 0x00ff); sw = apdu_send_le (slot, 0, 0x00, CMD_READ_BINARY, ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL, n, &buffer, &bufferlen); } if (*result && sw == SW_BAD_P0_P1) { /* Bad Parameter means that the offset is outside of the EF. When reading all data we take this as an indication for EOF. */ break; } if (sw != SW_SUCCESS && sw != SW_EOF_REACHED) { /* Make sure that pending buffers are released. */ xfree (buffer); xfree (*result); *result = NULL; *resultlen = 0; return map_sw (sw); } if (*result) /* Need to extend the buffer. */ { unsigned char *p = xtryrealloc (*result, *resultlen + bufferlen); if (!p) { gpg_error_t err = gpg_error_from_syserror (); xfree (buffer); xfree (*result); *result = NULL; *resultlen = 0; return err; } *result = p; memcpy (*result + *resultlen, buffer, bufferlen); *resultlen += bufferlen; xfree (buffer); buffer = NULL; } else /* Transfer the buffer into our result. */ { *result = buffer; *resultlen = bufferlen; } offset += bufferlen; if (offset > 32767) break; /* We simply truncate the result for too large files. */ if (nmax > bufferlen) nmax -= bufferlen; else nmax = 0; } while ((read_all && sw != SW_EOF_REACHED) || (!read_all && nmax)); return 0; } /* Perform a READ RECORD command. RECNO gives the record number to read with 0 indicating the current record. RECCOUNT must be 1 (not all cards support reading of more than one record). SHORT_EF should be 0 to read the current EF or contain a short EF. The result is stored in a newly allocated buffer at the address passed by RESULT. Returns the length of this data at the address of RESULTLEN. */ gpg_error_t iso7816_read_record (int slot, int recno, int reccount, int short_ef, unsigned char **result, size_t *resultlen) { int sw; unsigned char *buffer; size_t bufferlen; if (!result || !resultlen) return gpg_error (GPG_ERR_INV_VALUE); *result = NULL; *resultlen = 0; /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus we check for this limit. */ if (recno < 0 || recno > 255 || reccount != 1 || short_ef < 0 || short_ef > 254 ) return gpg_error (GPG_ERR_INV_VALUE); buffer = NULL; bufferlen = 0; sw = apdu_send_le (slot, 0, 0x00, CMD_READ_RECORD, recno, short_ef? short_ef : 0x04, -1, NULL, 0, &buffer, &bufferlen); if (sw != SW_SUCCESS && sw != SW_EOF_REACHED) { /* Make sure that pending buffers are released. */ xfree (buffer); xfree (*result); *result = NULL; *resultlen = 0; return map_sw (sw); } *result = buffer; *resultlen = bufferlen; return 0; } diff --git a/tests/gpgscm/main.c b/tests/gpgscm/main.c index 3191e0597..65929f007 100644 --- a/tests/gpgscm/main.c +++ b/tests/gpgscm/main.c @@ -1,303 +1,303 @@ /* TinyScheme-based test driver. * * Copyright (C) 2016 g10 code GmbH * * 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 "private.h" #include "scheme.h" #include "scheme-private.h" #include "ffi.h" -#include "i18n.h" +#include "../common/i18n.h" #include "../../common/argparse.h" #include "../../common/init.h" #include "../../common/logging.h" #include "../../common/strlist.h" #include "../../common/sysutils.h" #include "../../common/util.h" /* The TinyScheme banner. Unfortunately, it isn't in the header file. */ #define ts_banner "TinyScheme 1.41" int verbose; /* Constants to identify the commands and options. */ enum cmd_and_opt_values { aNull = 0, oVerbose = 'v', }; /* The list of commands and options. */ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")), ARGPARSE_end (), }; char *scmpath = ""; size_t scmpath_len = 0; /* Command line parsing. */ static void parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts) { int no_more_options = 0; while (!no_more_options && optfile_parse (NULL, NULL, NULL, pargs, popts)) { switch (pargs->r_opt) { case oVerbose: verbose++; break; default: pargs->err = 2; break; } } } /* Print usage information and provide strings for help. */ static const char * my_strusage( int level ) { const char *p; switch (level) { case 11: p = "gpgscm (@GNUPG@)"; break; case 13: p = VERSION; break; case 17: p = PRINTABLE_OS_NAME; break; case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break; case 1: case 40: p = _("Usage: gpgscm [options] [file] (-h for help)"); break; case 41: p = _("Syntax: gpgscm [options] [file]\n" "Execute the given Scheme program, or spawn interactive shell.\n"); break; default: p = NULL; break; } return p; } /* Load the Scheme program from FILE_NAME. If FILE_NAME is not an absolute path, and LOOKUP_IN_PATH is given, then it is qualified with the values in scmpath until the file is found. */ static gpg_error_t load (scheme *sc, char *file_name, int lookup_in_cwd, int lookup_in_path) { gpg_error_t err = 0; size_t n; const char *directory; char *qualified_name = file_name; int use_path; FILE *h = NULL; use_path = lookup_in_path && ! (file_name[0] == '/' || scmpath_len == 0); if (file_name[0] == '/' || lookup_in_cwd || scmpath_len == 0) { h = fopen (file_name, "r"); if (! h) err = gpg_error_from_syserror (); } if (h == NULL && use_path) for (directory = scmpath, n = scmpath_len; n; directory += strlen (directory) + 1, n--) { if (asprintf (&qualified_name, "%s/%s", directory, file_name) < 0) return gpg_error_from_syserror (); h = fopen (qualified_name, "r"); if (h) { err = 0; break; } if (n > 1) { free (qualified_name); continue; /* Try again! */ } err = gpg_error_from_syserror (); } if (h == NULL) { /* Failed and no more elements in scmpath to try. */ fprintf (stderr, "Could not read %s: %s.\n", qualified_name, gpg_strerror (err)); if (lookup_in_path) fprintf (stderr, "Consider using GPGSCM_PATH to specify the location " "of the Scheme library.\n"); goto leave; } if (verbose > 1) fprintf (stderr, "Loading %s...\n", qualified_name); scheme_load_named_file (sc, h, qualified_name); fclose (h); if (sc->retcode && sc->nesting) { fprintf (stderr, "%s: Unbalanced parenthesis\n", qualified_name); err = gpg_error (GPG_ERR_GENERAL); } leave: if (file_name != qualified_name) free (qualified_name); return err; } int main (int argc, char **argv) { int retcode; gpg_error_t err; char *argv0; ARGPARSE_ARGS pargs; scheme *sc; char *p; #if _WIN32 char pathsep = ';'; #else char pathsep = ':'; #endif char *script = NULL; /* Save argv[0] so that we can re-exec. */ argv0 = argv[0]; /* Parse path. */ if (getenv ("GPGSCM_PATH")) scmpath = getenv ("GPGSCM_PATH"); p = scmpath = strdup (scmpath); if (p == NULL) return 2; if (*p) scmpath_len++; for (; *p; p++) if (*p == pathsep) *p = 0, scmpath_len++; set_strusage (my_strusage); log_set_prefix ("gpgscm", GPGRT_LOG_WITH_PREFIX); /* Make sure that our subsystems are ready. */ i18n_init (); init_common_subsystems (&argc, &argv); if (!gcry_check_version (NEED_LIBGCRYPT_VERSION)) { fputs ("libgcrypt version mismatch\n", stderr); exit (2); } /* Parse the command line. */ pargs.argc = &argc; pargs.argv = &argv; pargs.flags = 0; parse_arguments (&pargs, opts); if (log_get_errorcount (0)) exit (2); sc = scheme_init_new_custom_alloc (gcry_malloc, gcry_free); if (! sc) { fprintf (stderr, "Could not initialize TinyScheme!\n"); return 2; } scheme_set_input_port_file (sc, stdin); scheme_set_output_port_file (sc, stderr); if (argc) { script = argv[0]; argc--, argv++; } err = load (sc, "init.scm", 0, 1); if (! err) err = load (sc, "ffi.scm", 0, 1); if (! err) err = ffi_init (sc, argv0, script ? script : "interactive", argc, (const char **) argv); if (! err) err = load (sc, "lib.scm", 0, 1); if (! err) err = load (sc, "repl.scm", 0, 1); if (! err) err = load (sc, "tests.scm", 0, 1); if (err) { fprintf (stderr, "Error initializing gpgscm: %s.\n", gpg_strerror (err)); exit (2); } if (script == NULL) { /* Interactive shell. */ fprintf (stderr, "gpgscm/"ts_banner".\n"); scheme_load_string (sc, "(interactive-repl)"); } else { err = load (sc, script, 1, 1); if (err) log_fatal ("%s: %s", script, gpg_strerror (err)); } retcode = sc->retcode; scheme_load_string (sc, "(*run-atexit-handlers*)"); scheme_deinit (sc); xfree (sc); return retcode; } diff --git a/tools/no-libgcrypt.c b/tools/no-libgcrypt.c index b56cc385d..873996889 100644 --- a/tools/no-libgcrypt.c +++ b/tools/no-libgcrypt.c @@ -1,162 +1,162 @@ /* no-libgcrypt.c - Replacement functions for libgcrypt. * Copyright (C) 2003 Free Software Foundation, Inc. * * This file is free software; as a special exception the author gives * unlimited permission to copy and/or distribute it, with or without * modifications, as long as this notice is preserved. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY, to the extent permitted by law; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. */ #include #include #include #include #include #include "../common/util.h" -#include "i18n.h" +#include "../common/i18n.h" /* Replace libgcrypt's malloc functions which are used by ../common/libcommon.a . ../common/util.h defines macros to map them to xmalloc etc. */ static void out_of_memory (void) { log_fatal (_("error allocating enough memory: %s\n"), strerror (errno)); } void * gcry_malloc (size_t n) { return malloc (n); } void * gcry_malloc_secure (size_t n) { return malloc (n); } void * gcry_xmalloc (size_t n) { void *p = malloc (n); if (!p) out_of_memory (); return p; } char * gcry_strdup (const char *string) { char *p = malloc (strlen (string)+1); if (p) strcpy (p, string); return p; } void * gcry_realloc (void *a, size_t n) { return realloc (a, n); } void * gcry_xrealloc (void *a, size_t n) { void *p = realloc (a, n); if (!p) out_of_memory (); return p; } void * gcry_calloc (size_t n, size_t m) { return calloc (n, m); } void * gcry_xcalloc (size_t n, size_t m) { void *p = calloc (n, m); if (!p) out_of_memory (); return p; } char * gcry_xstrdup (const char *string) { void *p = malloc (strlen (string)+1); if (!p) out_of_memory (); strcpy( p, string ); return p; } void gcry_free (void *a) { if (a) free (a); } /* We need this dummy because exechelp.c uses gcry_control to terminate the secure memeory. */ gcry_error_t gcry_control (enum gcry_ctl_cmds cmd, ...) { (void)cmd; return 0; } void gcry_set_outofcore_handler (gcry_handler_no_mem_t h, void *opaque) { (void)h; (void)opaque; } void gcry_set_fatalerror_handler (gcry_handler_error_t fnc, void *opaque) { (void)fnc; (void)opaque; } void gcry_set_log_handler (gcry_handler_log_t f, void *opaque) { (void)f; (void)opaque; } void gcry_create_nonce (void *buffer, size_t length) { (void)buffer; (void)length; log_fatal ("unexpected call to gcry_create_nonce\n"); } const char * gcry_cipher_algo_name (int algo) { (void)algo; return "?"; } diff --git a/tools/symcryptrun.c b/tools/symcryptrun.c index b32d43ac8..563e56bc3 100644 --- a/tools/symcryptrun.c +++ b/tools/symcryptrun.c @@ -1,1035 +1,1035 @@ /* symcryptrun.c - Tool to call simple symmetric encryption tools. * Copyright (C) 2005, 2007 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 . */ /* Sometimes simple encryption tools are already in use for a long time and there is a desire to integrate them into the GnuPG framework. The protocols and encryption methods might be non-standard or not even properly documented, so that a full-fledged encryption tool with an interface like gpg is not doable. This simple wrapper program provides a solution: It operates by calling the encryption/decryption module and providing the passphrase for a key (or even the key directly) using the standard pinentry mechanism through gpg-agent. */ /* This program is invoked in the following way: symcryptrun --class CLASS --program PROGRAM --keyfile KEYFILE \ [--decrypt | --encrypt] For encryption, the plain text must be provided on STDIN, and the ciphertext will be output to STDOUT. For decryption vice versa. CLASS can currently only be "confucius". PROGRAM must be the path to the crypto engine. KEYFILE must contain the secret key, which may be protected by a passphrase. The passphrase is retrieved via the pinentry program. The GPG Agent _must_ be running before starting symcryptrun. The possible exit status codes: 0 Success 1 Some error occurred 2 No valid passphrase was provided 3 The operation was canceled by the user Other classes may be added in the future. */ #define SYMC_BAD_PASSPHRASE 2 #define SYMC_CANCELED 3 #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_PTY_H #include #else #ifdef HAVE_TERMIOS_H #include #endif #ifdef HAVE_UTIL_H #include #endif #ifdef HAVE_LIBUTIL_H #include #endif #endif #ifdef HAVE_UTMP_H #include #endif #include #ifdef HAVE_LOCALE_H #include #endif #ifdef HAVE_LANGINFO_CODESET #include #endif #include -#include "i18n.h" +#include "../common/i18n.h" #include "../common/util.h" #include "../common/init.h" #include "../common/sysutils.h" /* FIXME: Bah. For spwq_secure_free. */ #define SIMPLE_PWQUERY_IMPLEMENTATION 1 #include "../common/simple-pwquery.h" /* From simple-gettext.c. */ /* We assume to have 'unsigned long int' value with at least 32 bits. */ #define HASHWORDBITS 32 /* The so called 'hashpjw' function by P.J. Weinberger [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools, 1986, 1987 Bell Telephone Laboratories, Inc.] */ static __inline__ ulong hash_string( const char *str_param ) { unsigned long int hval, g; const char *str = str_param; hval = 0; while (*str != '\0') { hval <<= 4; hval += (unsigned long int) *str++; g = hval & ((unsigned long int) 0xf << (HASHWORDBITS - 4)); if (g != 0) { hval ^= g >> (HASHWORDBITS - 8); hval ^= g; } } return hval; } /* Constants to identify the commands and options. */ enum cmd_and_opt_values { aNull = 0, oQuiet = 'q', oVerbose = 'v', oNoVerbose = 500, oOptions, oNoOptions, oLogFile, oHomedir, oClass, oProgram, oKeyfile, oDecrypt, oEncrypt, oInput }; /* The list of commands and options. */ static ARGPARSE_OPTS opts[] = { { 301, NULL, 0, N_("@\nCommands:\n ") }, { oDecrypt, "decrypt", 0, N_("decryption modus") }, { oEncrypt, "encrypt", 0, N_("encryption modus") }, { 302, NULL, 0, N_("@\nOptions:\n ") }, { oClass, "class", 2, N_("tool class (confucius)") }, { oProgram, "program", 2, N_("program filename") }, { oKeyfile, "keyfile", 2, N_("secret key file (required)") }, { oInput, "inputfile", 2, N_("input file name (default stdin)") }, { oVerbose, "verbose", 0, N_("verbose") }, { oQuiet, "quiet", 0, N_("quiet") }, { oLogFile, "log-file", 2, N_("use a log file for the server") }, { oOptions, "options" , 2, N_("|FILE|read options from FILE") }, /* Hidden options. */ { oNoVerbose, "no-verbose", 0, "@" }, { oHomedir, "homedir", 2, "@" }, { oNoOptions, "no-options", 0, "@" },/* shortcut for --options /dev/null */ {0} }; /* We keep all global options in the structure OPT. */ struct { int verbose; /* Verbosity level. */ int quiet; /* Be extra quiet. */ const char *homedir; /* Configuration directory name */ char *class; char *program; char *keyfile; char *input; } opt; /* Print usage information and provide strings for help. */ static const char * my_strusage (int level) { const char *p; switch (level) { case 11: p = "symcryptrun (@GNUPG@)"; break; case 13: p = VERSION; break; case 17: p = PRINTABLE_OS_NAME; break; case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break; case 1: case 40: p = _("Usage: symcryptrun [options] (-h for help)"); break; case 41: p = _("Syntax: symcryptrun --class CLASS --program PROGRAM " "--keyfile KEYFILE [options...] COMMAND [inputfile]\n" "Call a simple symmetric encryption tool\n"); break; case 31: p = "\nHome: "; break; case 32: p = gnupg_homedir (); break; case 33: p = "\n"; break; default: p = NULL; break; } return p; } /* This is in the GNU C library in unistd.h. */ #ifndef TEMP_FAILURE_RETRY /* Evaluate EXPRESSION, and repeat as long as it returns -1 with 'errno' set to EINTR. */ # define TEMP_FAILURE_RETRY(expression) \ (__extension__ \ ({ long int __result; \ do __result = (long int) (expression); \ while (__result == -1L && errno == EINTR); \ __result; })) #endif /* Unlink a file, and shred it if SHRED is true. */ int remove_file (char *name, int shred) { if (!shred) return unlink (name); else { int status; pid_t pid; pid = fork (); if (pid == 0) { /* Child. */ /* -f forces file to be writable, and -u unlinks it afterwards. */ char *args[] = { SHRED, "-uf", name, NULL }; execv (SHRED, args); _exit (127); } else if (pid < 0) { /* Fork failed. */ status = -1; } else { /* Parent. */ if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid) status = -1; } if (!WIFEXITED (status)) { log_error (_("%s on %s aborted with status %i\n"), SHRED, name, status); unlink (name); return 1; } else if (WEXITSTATUS (status)) { log_error (_("%s on %s failed with status %i\n"), SHRED, name, WEXITSTATUS (status)); unlink (name); return 1; } return 0; } } /* Class Confucius. "Don't worry that other people don't know you; worry that you don't know other people." Analects--1.16. */ /* Create temporary directory with mode 0700. Returns a dynamically allocated string with the filename of the directory. */ static char * confucius_mktmpdir (void) { char *name, *p; p = getenv ("TMPDIR"); if (!p || !*p) p = "/tmp"; if (p[strlen (p) - 1] == '/') name = xstrconcat (p, "gpg-XXXXXX", NULL); else name = xstrconcat (p, "/", "gpg-XXXXXX", NULL); if (!name || !gnupg_mkdtemp (name)) { log_error (_("can't create temporary directory '%s': %s\n"), name?name:"", strerror (errno)); return NULL; } return name; } /* Buffer size for I/O operations. */ #define CONFUCIUS_BUFSIZE 4096 /* Buffer size for output lines. */ #define CONFUCIUS_LINESIZE 4096 /* Copy the file IN to OUT, either of which may be "-". If PLAIN is true, and the copying fails, and OUT is not STDOUT, then shred the file instead unlinking it. */ static int confucius_copy_file (char *infile, char *outfile, int plain) { FILE *in; int in_is_stdin = 0; FILE *out; int out_is_stdout = 0; char data[CONFUCIUS_BUFSIZE]; ssize_t data_len; if (infile[0] == '-' && infile[1] == '\0') { /* FIXME: Is stdin in binary mode? */ in = stdin; in_is_stdin = 1; } else { in = fopen (infile, "rb"); if (!in) { log_error (_("could not open %s for writing: %s\n"), infile, strerror (errno)); return 1; } } if (outfile[0] == '-' && outfile[1] == '\0') { /* FIXME: Is stdout in binary mode? */ out = stdout; out_is_stdout = 1; } else { out = fopen (outfile, "wb"); if (!out) { log_error (_("could not open %s for writing: %s\n"), infile, strerror (errno)); return 1; } } /* Now copy the data. */ while ((data_len = fread (data, 1, sizeof (data), in)) > 0) { if (fwrite (data, 1, data_len, out) != data_len) { log_error (_("error writing to %s: %s\n"), outfile, strerror (errno)); goto copy_err; } } if (data_len < 0 || ferror (in)) { log_error (_("error reading from %s: %s\n"), infile, strerror (errno)); goto copy_err; } /* Close IN if appropriate. */ if (!in_is_stdin && fclose (in) && ferror (in)) { log_error (_("error closing %s: %s\n"), infile, strerror (errno)); goto copy_err; } /* Close OUT if appropriate. */ if (!out_is_stdout && fclose (out) && ferror (out)) { log_error (_("error closing %s: %s\n"), infile, strerror (errno)); goto copy_err; } return 0; copy_err: if (!out_is_stdout) remove_file (outfile, plain); return 1; } /* Get a passphrase in secure storage (if possible). If AGAIN is true, then this is a repeated attempt. If CANCELED is not a null pointer, it will be set to true or false, depending on if the user canceled the operation or not. On error (including cancelation), a null pointer is returned. The passphrase must be deallocated with confucius_drop_pass. CACHEID is the ID to be used for passphrase caching and can be NULL to disable caching. */ char * confucius_get_pass (const char *cacheid, int again, int *canceled) { int err; char *pw; char *orig_codeset; if (canceled) *canceled = 0; orig_codeset = i18n_switchto_utf8 (); pw = simple_pwquery (cacheid, again ? _("does not match - try again"):NULL, _("Passphrase:"), NULL, 0, &err); i18n_switchback (orig_codeset); if (!pw) { if (err) log_error (_("error while asking for the passphrase: %s\n"), gpg_strerror (err)); else { log_info (_("cancelled\n")); if (canceled) *canceled = 1; } } return pw; } /* Drop a passphrase retrieved with confucius_get_pass. */ void confucius_drop_pass (char *pass) { if (pass) spwq_secure_free (pass); } /* Run a confucius crypto engine. If MODE is oEncrypt, encryption is requested. If it is oDecrypt, decryption is requested. INFILE and OUTFILE are the temporary files used in the process. */ int confucius_process (int mode, char *infile, char *outfile, int argc, char *argv[]) { char **args; int cstderr[2]; int master; int slave; int res; pid_t pid; pid_t wpid; int tries = 0; char cacheid[40]; signal (SIGPIPE, SIG_IGN); if (!opt.program) { log_error (_("no --program option provided\n")); return 1; } if (mode != oDecrypt && mode != oEncrypt) { log_error (_("only --decrypt and --encrypt are supported\n")); return 1; } if (!opt.keyfile) { log_error (_("no --keyfile option provided\n")); return 1; } /* Generate a hash from the keyfile name for caching. */ snprintf (cacheid, sizeof (cacheid), "confucius:%lu", hash_string (opt.keyfile)); cacheid[sizeof (cacheid) - 1] = '\0'; args = malloc (sizeof (char *) * (10 + argc)); if (!args) { log_error (_("cannot allocate args vector\n")); return 1; } args[0] = opt.program; args[1] = (mode == oEncrypt) ? "-m1" : "-m2"; args[2] = "-q"; args[3] = infile; args[4] = "-z"; args[5] = outfile; args[6] = "-s"; args[7] = opt.keyfile; args[8] = (mode == oEncrypt) ? "-af" : "-f"; args[9 + argc] = NULL; while (argc--) args[9 + argc] = argv[argc]; if (pipe (cstderr) < 0) { log_error (_("could not create pipe: %s\n"), strerror (errno)); free (args); return 1; } if (openpty (&master, &slave, NULL, NULL, NULL) == -1) { log_error (_("could not create pty: %s\n"), strerror (errno)); close (cstderr[0]); close (cstderr[1]); free (args); return -1; } /* We don't want to deal with the worst case scenarios. */ assert (master > 2); assert (slave > 2); assert (cstderr[0] > 2); assert (cstderr[1] > 2); pid = fork (); if (pid < 0) { log_error (_("could not fork: %s\n"), strerror (errno)); close (master); close (slave); close (cstderr[0]); close (cstderr[1]); free (args); return 1; } else if (pid == 0) { /* Child. */ /* Close the parent ends. */ close (master); close (cstderr[0]); /* Change controlling terminal. */ if (login_tty (slave)) { /* It's too early to output a debug message. */ _exit (1); } dup2 (cstderr[1], 2); close (cstderr[1]); /* Now kick off the engine program. */ execv (opt.program, args); log_error (_("execv failed: %s\n"), strerror (errno)); _exit (1); } else { /* Parent. */ char buffer[CONFUCIUS_LINESIZE]; int buffer_len = 0; fd_set fds; int slave_closed = 0; int stderr_closed = 0; close (slave); close (cstderr[1]); free (args); /* Listen on the output FDs. */ do { FD_ZERO (&fds); if (!slave_closed) FD_SET (master, &fds); if (!stderr_closed) FD_SET (cstderr[0], &fds); res = select (FD_SETSIZE, &fds, NULL, NULL, NULL); if (res < 0) { log_error (_("select failed: %s\n"), strerror (errno)); kill (pid, SIGTERM); close (master); close (cstderr[0]); return 1; } if (FD_ISSET (cstderr[0], &fds)) { /* We got some output on stderr. This is just passed through via the logging facility. */ res = read (cstderr[0], &buffer[buffer_len], sizeof (buffer) - buffer_len - 1); if (res < 0) { log_error (_("read failed: %s\n"), strerror (errno)); kill (pid, SIGTERM); close (master); close (cstderr[0]); return 1; } else { char *newline; buffer_len += res; for (;;) { buffer[buffer_len] = '\0'; newline = strchr (buffer, '\n'); if (newline) { *newline = '\0'; log_error ("%s\n", buffer); buffer_len -= newline + 1 - buffer; memmove (buffer, newline + 1, buffer_len); } else if (buffer_len == sizeof (buffer) - 1) { /* Overflow. */ log_error ("%s\n", buffer); buffer_len = 0; } else break; } if (res == 0) stderr_closed = 1; } } else if (FD_ISSET (master, &fds)) { char data[512]; res = read (master, data, sizeof (data)); if (res < 0) { if (errno == EIO) { /* Slave-side close leads to readable fd and EIO. */ slave_closed = 1; } else { log_error (_("pty read failed: %s\n"), strerror (errno)); kill (pid, SIGTERM); close (master); close (cstderr[0]); return 1; } } else if (res == 0) /* This never seems to be what happens on slave-side close. */ slave_closed = 1; else { /* Check for password prompt. */ if (data[res - 1] == ':') { char *pass; int canceled; /* If this is not the first attempt, the passphrase seems to be wrong, so clear the cache. */ if (tries) simple_pwclear (cacheid); pass = confucius_get_pass (cacheid, tries ? 1 : 0, &canceled); if (!pass) { kill (pid, SIGTERM); close (master); close (cstderr[0]); return canceled ? SYMC_CANCELED : 1; } write (master, pass, strlen (pass)); write (master, "\n", 1); confucius_drop_pass (pass); tries++; } } } } while (!stderr_closed || !slave_closed); close (master); close (cstderr[0]); wpid = waitpid (pid, &res, 0); if (wpid < 0) { log_error (_("waitpid failed: %s\n"), strerror (errno)); kill (pid, SIGTERM); /* State of cached password is unclear. Just remove it. */ simple_pwclear (cacheid); return 1; } else { /* Shouldn't happen, as we don't use WNOHANG. */ assert (wpid != 0); if (!WIFEXITED (res)) { log_error (_("child aborted with status %i\n"), res); /* State of cached password is unclear. Just remove it. */ simple_pwclear (cacheid); return 1; } if (WEXITSTATUS (res)) { /* The passphrase was wrong. Remove it from the cache. */ simple_pwclear (cacheid); /* We probably exceeded our number of attempts at guessing the password. */ if (tries >= 3) return SYMC_BAD_PASSPHRASE; else return 1; } return 0; } } /* Not reached. */ } /* Class confucius main program. If MODE is oEncrypt, encryption is requested. If it is oDecrypt, decryption is requested. The other parameters are taken from the global option data. */ int confucius_main (int mode, int argc, char *argv[]) { int res; char *tmpdir; char *infile; int infile_from_stdin = 0; char *outfile; tmpdir = confucius_mktmpdir (); if (!tmpdir) return 1; if (opt.input && !(opt.input[0] == '-' && opt.input[1] == '\0')) infile = xstrdup (opt.input); else { infile_from_stdin = 1; /* TMPDIR + "/" + "in" + "\0". */ infile = malloc (strlen (tmpdir) + 1 + 2 + 1); if (!infile) { log_error (_("cannot allocate infile string: %s\n"), strerror (errno)); rmdir (tmpdir); return 1; } strcpy (infile, tmpdir); strcat (infile, "/in"); } /* TMPDIR + "/" + "out" + "\0". */ outfile = malloc (strlen (tmpdir) + 1 + 3 + 1); if (!outfile) { log_error (_("cannot allocate outfile string: %s\n"), strerror (errno)); free (infile); rmdir (tmpdir); return 1; } strcpy (outfile, tmpdir); strcat (outfile, "/out"); if (infile_from_stdin) { /* Create INFILE and fill it with content. */ res = confucius_copy_file ("-", infile, mode == oEncrypt); if (res) { free (outfile); free (infile); rmdir (tmpdir); return res; } } /* Run the engine and thus create the output file, handling passphrase retrieval. */ res = confucius_process (mode, infile, outfile, argc, argv); if (res) { remove_file (outfile, mode == oDecrypt); if (infile_from_stdin) remove_file (infile, mode == oEncrypt); free (outfile); free (infile); rmdir (tmpdir); return res; } /* Dump the output file to stdout. */ res = confucius_copy_file (outfile, "-", mode == oDecrypt); if (res) { remove_file (outfile, mode == oDecrypt); if (infile_from_stdin) remove_file (infile, mode == oEncrypt); free (outfile); free (infile); rmdir (tmpdir); return res; } remove_file (outfile, mode == oDecrypt); if (infile_from_stdin) remove_file (infile, mode == oEncrypt); free (outfile); free (infile); rmdir (tmpdir); return 0; } /* symcryptrun's entry point. */ int main (int argc, char **argv) { ARGPARSE_ARGS pargs; int orig_argc; char **orig_argv; FILE *configfp = NULL; char *configname = NULL; unsigned configlineno; int mode = 0; int res; char *logfile = NULL; int default_config = 1; early_system_init (); set_strusage (my_strusage); log_set_prefix ("symcryptrun", GPGRT_LOG_WITH_PREFIX); /* Make sure that our subsystems are ready. */ i18n_init(); init_common_subsystems (&argc, &argv); /* Check whether we have a config file given on the commandline */ orig_argc = argc; orig_argv = argv; pargs.argc = &argc; pargs.argv = &argv; pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */ while (arg_parse( &pargs, opts)) { if (pargs.r_opt == oOptions) { /* Yes there is one, so we do not try the default one, but read the option file when it is encountered at the commandline */ default_config = 0; } else if (pargs.r_opt == oNoOptions) default_config = 0; /* --no-options */ else if (pargs.r_opt == oHomedir) gnupg_set_homedir (pargs.r.ret_str); } if (default_config) configname = make_filename (gnupg_homedir (), "symcryptrun.conf", NULL ); argc = orig_argc; argv = orig_argv; pargs.argc = &argc; pargs.argv = &argv; pargs.flags= 1; /* do not remove the args */ next_pass: if (configname) { configlineno = 0; configfp = fopen (configname, "r"); if (!configfp) { if (!default_config) { log_error (_("option file '%s': %s\n"), configname, strerror(errno) ); exit(1); } xfree (configname); configname = NULL; } default_config = 0; } /* Parse the command line. */ while (optfile_parse (configfp, configname, &configlineno, &pargs, opts)) { switch (pargs.r_opt) { case oDecrypt: mode = oDecrypt; break; case oEncrypt: mode = oEncrypt; break; case oQuiet: opt.quiet = 1; break; case oVerbose: opt.verbose++; break; case oNoVerbose: opt.verbose = 0; break; case oClass: opt.class = pargs.r.ret_str; break; case oProgram: opt.program = pargs.r.ret_str; break; case oKeyfile: opt.keyfile = pargs.r.ret_str; break; case oInput: opt.input = pargs.r.ret_str; break; case oLogFile: logfile = pargs.r.ret_str; break; case oOptions: /* Config files may not be nested (silently ignore them) */ if (!configfp) { xfree(configname); configname = xstrdup(pargs.r.ret_str); goto next_pass; } break; case oNoOptions: break; /* no-options */ case oHomedir: /* Ignore this option here. */; break; default : pargs.err = configfp? 1:2; break; } } if (configfp) { fclose( configfp ); configfp = NULL; configname = NULL; goto next_pass; } xfree (configname); configname = NULL; if (!mode) log_error (_("either %s or %s must be given\n"), "--decrypt", "--encrypt"); if (log_get_errorcount (0)) exit (1); if (logfile) log_set_file (logfile); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); setup_libgcrypt_logging (); gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); /* Tell simple-pwquery about the standard socket name. */ { char *tmp = make_filename (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL); simple_pw_set_socket (tmp); xfree (tmp); } if (!opt.class) { log_error (_("no class provided\n")); res = 1; } else if (!strcmp (opt.class, "confucius")) { res = confucius_main (mode, argc, argv); } else { log_error (_("class %s is not supported\n"), opt.class); res = 1; } return res; }