From e9f297251e9ec7acf257de1b3c6ea6eec0acab05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Ates?= Date: Fri, 9 Jan 2015 17:06:50 +0100 Subject: [PATCH] Application. --- cv2extractor/cv2extractor.c | 516 ++++++++++++++++++++++++++++++++++++ 1 file changed, 516 insertions(+) create mode 100644 cv2extractor/cv2extractor.c diff --git a/cv2extractor/cv2extractor.c b/cv2extractor/cv2extractor.c new file mode 100644 index 0000000..cdd9192 --- /dev/null +++ b/cv2extractor/cv2extractor.c @@ -0,0 +1,516 @@ +/* $Id$ + * + * cv2extractor - A free implementation of a reader of Carte Vitale 2 cards. + * + * Copyright (C) 2015 Entr'ouvert + * https://dev.entrouvert.org/projects/cv2extractor + * + * Authors: See AUTHORS file in top-level directory. + * + * This program 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "api_lec.h" +#include "api_err.h" + +#include "config.h" +#include "errors.h" +#include "utils.h" + + +int send_file(gchar* filename) { + int rc = CV2EXTRACTOR_ERROR_UNDEFINED; + int command_rc; + gchar* command; + if (SSL_TRANSFER_SECRET != NULL && SSL_TRANSFER_SECRET[0] != '\0') { + command = g_strdup_printf("sshpass -p '%s' scp %s %s", SSL_TRANSFER_SECRET, filename, SSL_TRANSFER_TARGET); + } else { + command = g_strdup_printf("scp %s %s", filename, SSL_TRANSFER_TARGET); + } + command_rc = system(command); + goto_cleanup_if_fail_with_rc_with_warning(command_rc != -1, CV2EXTRACTOR_ERROR_SSH_COMMAND); + rc = CV2EXTRACTOR_NO_ERROR; +cleanup: + g_free(command); + return rc; +} + +gchar* get_timestamped_filename() { + gchar* filename; + gchar str_date[24]; + time_t t = time(NULL); + struct tm tm = *localtime(&t); + strftime(str_date, sizeof(str_date), "%Y-%d-%m-%H-%M-%S.xml", &tm); + if (!cv2extractor_strisempty(XML_FILE_PREFIX)) { + filename = g_strconcat(XML_FILE_PREFIX, str_date, NULL); + } else { + filename = str_date; + } + return filename; +} + +int write_file(gchar* filename, gchar* stream, unsigned int size) { + int rc = CV2EXTRACTOR_ERROR_UNDEFINED; + FILE *file; + file = fopen(filename, "w+"); + if(file == NULL) { + g_printf("\n! Error opening file.\n"); + goto_cleanup_with_rc_with_critical("Error opening file", CV2EXTRACTOR_ERROR_OPENING_FILE); + } + size_t w_check; + w_check = fwrite(stream, 1, size, file); + if (w_check != size) { + g_printf("\n! Error writing file.\n"); + goto_cleanup_with_rc_with_critical("Error writing file", CV2EXTRACTOR_ERROR_WRITING_FILE); + } + if (fclose(file) != CV2EXTRACTOR_NO_ERROR) { + g_printf("\n! Error closing file.\n"); + goto_cleanup_with_rc_with_critical("Error closing file", CV2EXTRACTOR_ERROR_CLOSING_FILE); + } + rc = CV2EXTRACTOR_NO_ERROR; +cleanup: + return rc; +} + +int main(){ + g_type_init(); + + int rc = CV2EXTRACTOR_ERROR_UNDEFINED; + + g_printf("------------------------\n"); + g_printf("----- cv2extractor -----\n"); + g_printf("------------------------\n\n"); + + + /* + * Read configuration file + */ + + g_printf("Reading configuration...\n"); + g_printf("\t--> XML files will be written in : %s\n", XML_FILES_PATH); + if (SSL_TRANSFER_TARGET != NULL && SSL_TRANSFER_TARGET[0] != '\0') { + g_printf("\t--> XML files will be sent using ssh to : %s\n", SSL_TRANSFER_TARGET); + } + g_printf("\n"); + + + gchar* filename; + + /* + * Variables used by many library functions + */ + + /* Hn_Init */ + /* Reader behavior */ + unsigned short pusMode = FLIP_FLOP; + + /* Hn_TestPresence */ + /* Reader state */ + unsigned char pucStatut; + + /* Hn_LectureVitale */ + /* Will store the Carte Vitale content */ + gchar pcDataOut[BUFFER_SIZE]; + /* Size of the sapec allocated for Carte Vitale content and sizeof the content read */ + unsigned int puiLgDataOut; + + /*Hn_IntroductionMstCPS */ + /**/ + gchar tcCodeCivilite[3]; + tcCodeCivilite[2] = '\0'; + /**/ + gchar szNomPatronymique[28]; + szNomPatronymique[27] = '\0'; + /* Will contain the value of bad PIn code entered in the CPS */ + gchar pcPresentPINCode; + + /* Hn_ControleCPS */ + /* The pin code user entered, 1234 for cps testing cards */ + gchar* tcPINCode; + /**/ + gchar tcDateFinValidite[9]; + tcDateFinValidite[8] = '\0'; + /**/ + gchar tcTypeCarte[3]; + tcTypeCarte[2] = '\0'; + /**/ + gchar tcCategorie[3]; + tcCategorie[2] = '\0'; + + /* Use at card reading */ + short psEtatCarteCV; + /* Use at card reading */ + short psEtatCarteCPS; + + /* Function functionnal error code */ + unsigned short pusCodeErreur = 0; + + /* Function execution error code */ + unsigned short err = 0; + + + /* + * API VITALE Session Opening. + */ + + g_printf("Connect API VITALE library with the reader...\n"); + err = Hn_Init(NULL , &pusMode, &pusCodeErreur); + if(err == CV2EXTRACTOR_NO_ERROR) { + g_printf("\t--> Reader connected.\n"); + } else { + if(err == WAR_APICPS_NON_DISPONIBLE) { + g_printf("\t--> Reader connected.\n"); + g_printf("\n--> Error loading CPS library, continue.\n"); + } + else{ + g_printf("\nError during connection (Hn_Init error %d)\n", err); + goto_cleanup_with_rc_with_critical("Error during connection (Hn_Init error)", err); + } + } + g_printf("\n"); + + + /* + * Reading loop + */ + + g_printf("<-------- Please insert card -------->\n"); + + do { + /* + * Wait for card insertion + * + * Use of Hn_TestPresence to test insertion. If nothing inserted, + * sleep and retest. Else, read. + * + * COUPLEUR_VITALE does not matter since err is equal to 0 whatever + * is put in the reader. + * + */ + err = Hn_TestPresence(COUPLEUR_VITALE , &pucStatut, &pusCodeErreur); + if(err == CV2EXTRACTOR_NO_ERROR && pucStatut == ETAT_CPRESENTE) { + + /* + * Test the card inserted type. + * + * Maybe there's a way to know what kind of card it is using + * pusCodeErreur. But nothing clear from the doc. + * + * So we try to read it as a Carte Vitale. If it fails, as a CPS. + * Else, whatever. + */ + + g_printf("\t--> Card inserted.\n"); + + /* + * Try to read a Carte vitale + */ + + /* puiLgDataOut gives the buffer size in input but also gives + the size of the file read in output. It is thus required to set it + before each reading */ + puiLgDataOut = BUFFER_SIZE; + err = Hn_LectureVitale(0, pcDataOut, &puiLgDataOut, &psEtatCarteCV, &pusCodeErreur); + if(err == CV2EXTRACTOR_NO_ERROR || err == WAR_DONNEES_ADM) { + + /* + * Carte Vitale inserted + */ + + g_printf("\t--> Carte Vitale inserted.\n"); + if(err == WAR_DONNEES_ADM) { + g_printf("\t--> Only administrative data have been read.\n"); + } + g_printf("%s\n", pcDataOut); + + /* + * Write XML file. + */ + + g_printf("\t--> The data read is %d bytes.\n", puiLgDataOut); + cv2extractor_message("The data read is %d bytes.", puiLgDataOut); + + filename = get_timestamped_filename(); + int write_err; + write_err = write_file(filename, pcDataOut, puiLgDataOut); + if (write_err != CV2EXTRACTOR_NO_ERROR) { + g_printf("\n! Error handling file.\n"); + goto_cleanup_with_rc_with_critical("Error handling file", write_err); + } + g_printf("--> Write: %s\n", filename); + cv2extractor_message("Write file %s.", filename); + + /* + * Send XML file. + */ + + if (SSL_TRANSFER_TARGET != NULL && SSL_TRANSFER_TARGET[0] != '\0') { + int ret = 0; + ret = send_file(filename); + if(ret) { + g_printf("\n! Error sending file with error: %d.\n", ret); + cv2extractor_warning("Error sending file with error: %d.", ret); + } else { + g_printf("--> File sent.\n"); + cv2extractor_message("File sent using ssh."); + } + } + + /* + * Wait for card removal. + */ + + g_printf("<------ Please remove CV2 card ------>\n"); + do { + err = Hn_TestPresence(COUPLEUR_VITALE , &pucStatut, &pusCodeErreur); + if(err != CV2EXTRACTOR_NO_ERROR) { + g_printf("\nError testing for Carte Vitale removal (Hn_TestPresence error %d)\n", err); + cv2extractor_warning("Error testing for Carte Vitale removal (Hn_TestPresence error %d).", err); + //goto_cleanup_with_rc_with_critical("Error testing for Carte Vitale removal (Hn_TestPresence error)", err); + } + } while(pucStatut == ETAT_CPRESENTE); + g_printf("<-------- Please insert card -------->\n"); + + } else if(err == ERR_CARTE_INCONNUE) { + + /* It is not a a Carte vitale, try to read a CPS */ + err = Hn_IntroductionMstCPS(0, tcCodeCivilite, szNomPatronymique, &pcPresentPINCode, &psEtatCarteCPS, &pusCodeErreur); + if (err == CV2EXTRACTOR_NO_ERROR) { + + /* + * Carte CPS inserted + */ + + g_printf("\t--> CPS card inserted.\n"); + g_printf("\t--> tcCodeCivilite: %s\n", tcCodeCivilite); + g_printf("\t--> szNomPatronymique: %s\n", szNomPatronymique); + g_printf("\t--> vecteur de retour %d\n", psEtatCarteCPS); + + /* + * Check CPS card status + */ + + short bit_0, bit_1, bit_2, bit_3, bit_4, bit_5, bit_6; + bit_0 = psEtatCarteCPS & 0x0001; + bit_1 = psEtatCarteCPS & 0x0002; + bit_2 = psEtatCarteCPS & 0x0004; + bit_3 = psEtatCarteCPS & 0x0008; + bit_4 = psEtatCarteCPS & 0x0010; + bit_5 = psEtatCarteCPS & 0x0020; + bit_6 = psEtatCarteCPS & 0x0040; + + if(bit_6 == CPS_DETEST){ + g_printf("\t--> CPS for testing. Can only unlock Carte Vitale for testing.\n"); + } + + if(bit_5 == CPS_NONAUTORISEE){ + g_printf("\t--> This card don't unlock Carte Vitale cards.\n"); + } else { + g_printf("\t--> This card unlock Carte Vitale cards.\n"); + } + + int valid = 1; + + if (bit_1 == CPS_BLOQUEE){ + g_printf("\t--> CPS locked.\n"); + cv2extractor_warning("CPS locked (%s, %s).", tcCodeCivilite, szNomPatronymique); + valid = 0; + } + if (bit_3 == CPS_HORSVALIDITE) { + g_printf("\t--> CPS invalid.\n"); + cv2extractor_warning("CPS invalid (%s, %s).", tcCodeCivilite, szNomPatronymique); + valid = 0; + } + if (bit_4 == CPS_IDENTINCORRECT){ + g_printf("\t--> CPS missing application.\n"); + cv2extractor_warning("CPS missing application (%s, %s).", tcCodeCivilite, szNomPatronymique); + valid = 0; + } + + if (bit_2 == CPS_CODE_ACTIF) { + g_printf("\t--> The card is already unlocked\n"); + } else if (valid == 1) { + + /* + * The card is valid. Ask for PIN code to unlock the + * CPS card. + */ + + if ((pcPresentPINCode != '0') && (pcPresentPINCode != '1') && (pcPresentPINCode != '2')) { + g_printf("\t--> The bad PIN code value is invalid: (%c). That should not happen, we don't pursue with this card.\n", pcPresentPINCode); + } else { + if (CPS_PIN_CODE == NULL || CPS_PIN_CODE[0] == '\0') { + + /* + * User interactive mode + */ + + int tries; + tries = PIN_TRIES - (pcPresentPINCode - '0'); + int unlock = 0; + size_t max_bytes = PIN_DIGITS; + size_t read_bytes = 0; + tcPINCode = (char*) malloc(PIN_DIGITS + 1); + do { + + /* + * Get user entry from stdin + */ + + g_printf("<------ Enter the PIN code (Remaining %d attemps) ------>\n", tries); + do { + read_bytes = getline(&tcPINCode, &max_bytes, stdin); + if (read_bytes != (PIN_DIGITS + 1)) { + g_printf("\t--> A PIN code is %d digit long.\n", PIN_DIGITS); + } + } while (read_bytes != (PIN_DIGITS + 1)); + + /* + * Try to unlock CPS card with the pin + */ + + err = Hn_ControleCPS(tcPINCode, tcDateFinValidite, tcTypeCarte, &pcPresentPINCode, tcCategorie, &pusCodeErreur); + if(err == CV2EXTRACTOR_NO_ERROR) { + /* CPS unlocked */ + unlock = 1; + g_printf("\t--> CPS unlocked\n"); + g_printf("\t--> tcDateFinValidite: %s\n", tcDateFinValidite); + g_printf("\t--> tcTypeCarte: %s\n", tcTypeCarte); + g_printf("\t--> pcPresentPINCode: %c\n", pcPresentPINCode); + g_printf("\t--> tcCategorie: %s\n", tcCategorie); + } else if (err == ERR_PINCODE_INACTIF) { + g_printf("\t--> The PIN code is invalid\n"); + tries = PIN_TRIES - (pcPresentPINCode - '0'); + } else { + g_printf("\t--> Error with PIN validation\n"); + g_warning("Error processing PIN validation (Hn_ControleCPS: %d).", err); + tries = PIN_TRIES - (pcPresentPINCode - '0'); + } + } while(tries > 0 && unlock == 0); + g_free(tcPINCode); + + } else if (sizeof(CPS_PIN_CODE) == (PIN_DIGITS + 1)) { + tcPINCode = CPS_PIN_CODE; + + /* + * Try to unlock CPS card with the pin + */ + + err = Hn_ControleCPS(tcPINCode, tcDateFinValidite, tcTypeCarte, &pcPresentPINCode, tcCategorie, &pusCodeErreur); + if(err == CV2EXTRACTOR_NO_ERROR) { + /* CPS unlocked */ + g_printf("\t--> CPS unlocked\n"); + g_printf("\t--> tcDateFinValidite: %s\n", tcDateFinValidite); + g_printf("\t--> tcTypeCarte: %s\n", tcTypeCarte); + g_printf("\t--> pcPresentPINCode: %c\n", pcPresentPINCode); + g_printf("\t--> tcCategorie: %s\n", tcCategorie); + } else if (err == ERR_PINCODE_INACTIF) { + g_printf("\t--> The PIN code is invalid\n"); + g_warning("The PIN code recorded is invalid."); + } else { + g_printf("\t--> Error with PIN validation\n"); + g_warning("rror processing PIN validation (Hn_ControleCPS: %d).", err); + } + } else { + g_printf("\t--> A PIN code is recorded to avoid interactive mode but the length is not %d digits.\n", PIN_DIGITS); + cv2extractor_critical("A PIN code is recorded to avoid interactive mode but the length is not %d digits.", PIN_DIGITS); + } + } + } + + /* + * Wait for card removal. + */ + + g_printf("<------ Please remove CPS card ------>\n"); + short sTempsAttente = CPS_WAITING_TIME; + err = Hn_RetraitMhtCPS(sTempsAttente, &pusCodeErreur); + if(err != CV2EXTRACTOR_NO_ERROR) { + g_printf("\nError testing for CPS removal (Hn_TestPresence error %d)\n", err); + cv2extractor_warning("Error testing for CPS removal (Hn_TestPresence error %d).", err); + //goto_cleanup_with_rc_with_critical("Error testing for CPS Vitale removal (Hn_TestPresence error)", err); + } + g_printf("<-------- Please insert card -------->\n"); + + } else if (err == ERR_CARTE_INCONNUE) { + + /* + * Somebody introduced something unexpected in the reader... + */ + + g_printf("Unknown card type."); + cv2extractor_warning("Unknown card type."); + + } else { + g_printf("\nError trying to read a CPS (Hn_IntroductionMstCPS error %d)\n", err); + cv2extractor_warning("Error trying to read a CPS (Hn_IntroductionMstCPS error %d).", err); + //goto_cleanup_with_rc_with_critical("Error trying to read a CPS (Hn_IntroductionMstCPS error)", err); + } + + } else { + g_printf("\nError trying to read a Carte Vitale (Hn_LectureVitale error %d) - Please retry\n", err); + cv2extractor_warning("Error trying to read a Carte Vitale (Hn_LectureVitale error %d).", err); + //goto_cleanup_with_rc_with_critical("Error trying to read a Carte Vitale (Hn_LectureVitale error)", err); + } + + } else if(err != CV2EXTRACTOR_NO_ERROR) { + g_printf("\nError testing for card insertion (Hn_TestPresence error %d)\n", err); + cv2extractor_warning("Error testing for card insertion (Hn_TestPresence error %d).", err); + //goto_cleanup_with_rc_with_critical("Error testing for card insertion (Hn_TestPresence error)", err); + } + + sleep(TIME_SLEEP); + } while(TRUE); + + + /* + * API VITALE Session closing. + * + * Not used. Exit loop not yet implemented. + */ + + g_printf("Disconnect API VITALE library of the reader...\n"); + err = Hn_Finir(&pusCodeErreur); + if(err == CV2EXTRACTOR_NO_ERROR) { + g_printf("\t--> Reader disconnected.\n"); + } else { + if(err == WAR_APICPS_NON_DISPONIBLE) { + g_printf("\t--> Reader disconnected.\n"); + g_printf("\n--> Error loading CPS library, continue.\n"); + } + else{ + g_printf("\nError %d during disconnection, exit (Hn_Init)!\n", err); + goto_cleanup_with_rc_with_critical("Error %d during disconnection, exit (Hn_Init)!", err); + } + } + g_printf("\n"); + + + rc = CV2EXTRACTOR_NO_ERROR; + +cleanup: + /* Cleaning... */ + return rc; +}