From 0d4dd2fea128b5c8e72e9d8f49aabad3cf548a85 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Mon, 16 Jun 2014 13:19:48 +0200 Subject: [PATCH] start moving code to the library, and add error codes --- .gitignore | 2 + configure.ac | 1 + lib/Makefile.am | 1 + lib/error.c | 95 +++++++++++++++++++++++++++++++++++++++++ lib/internal.h | 20 ++++++++- lib/ykpiv.c | 109 +++++++++++++++++++++++++++++++++++++++--------- lib/ykpiv.h | 16 ++++++- lib/ykpiv.map | 2 + 8 files changed, 222 insertions(+), 24 deletions(-) create mode 100644 lib/error.c diff --git a/.gitignore b/.gitignore index 7a20b20..a4cf1e3 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,8 @@ yubico-piv-tool-*-mac.zip.sig tests/basic.sh.log tests/basic.sh.trs tests/test-suite.log +lib/error.lo +lib/error.o lib/libykpiv.la lib/version.lo lib/version.o diff --git a/configure.ac b/configure.ac index 225bc03..9184a28 100644 --- a/configure.ac +++ b/configure.ac @@ -136,6 +136,7 @@ if test "$gl_gcc_warnings" = yes; then nw="$nw -Wtraditional-conversion" # Too many warnings for now nw="$nw -Wconversion" # Too many warnings for now nw="$nw -Wsuggest-attribute=pure" # Is it worth using attributes? + nw="$nw -Wsuggest-attribute=const" # Is it worth using attributes? gl_MANYWARN_ALL_GCC([ws]) gl_MANYWARN_COMPLEMENT(ws, [$ws], [$nw]) diff --git a/lib/Makefile.am b/lib/Makefile.am index 72b2229..ee448ab 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -30,6 +30,7 @@ AM_CPPFLAGS = $(OPENSSL_CFLAGS) $(PCSC_CFLAGS) lib_LTLIBRARIES = libykpiv.la libykpiv_la_SOURCES = ykpiv.c version.c ykpiv.pc.in ykpiv.map internal.h +libykpiv_la_SOURCES += error.c libykpiv_la_includedir = $(includedir)/ykpiv libykpiv_la_include_HEADERS = ykpiv.h ykpiv-version.h diff --git a/lib/error.c b/lib/error.c new file mode 100644 index 0000000..12da084 --- /dev/null +++ b/lib/error.c @@ -0,0 +1,95 @@ + /* + * Copyright (c) 2014 Yubico AB + * All rights reserved. + * + * 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, see . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, We grant you additional + * permission to convey the resulting work. Corresponding Source for a + * non-source form of such a combination shall include the source code + * for the parts of OpenSSL used as well as that of the covered work. + * + */ + +#include "ykpiv.h" + +#include + +#define ERR(name, desc) { name, #name, desc } + +typedef struct +{ + ykpiv_rc rc; + const char *name; + const char *description; +} err_t; + +static const err_t errors[] = { + ERR (YKPIV_OK, "Successful return"), + ERR (YKPIV_MEMORY_ERROR, "Error allocating memory"), + ERR (YKPIV_PCSC_ERROR, "Error in PCSC call"), +}; + +/** + * ykpiv_strerror: + * @err: error code + * + * Convert return code to human readable string explanation of the + * reason for the particular error code. + * + * This string can be used to output a diagnostic message to the user. + * + * Return value: Returns a pointer to a statically allocated string + * containing an explanation of the error code @err. + **/ +const char *ykpiv_strerror(ykpiv_rc err) { + static const char *unknown = "Unknown ykpiv error"; + const char *p; + + if (-err < 0 || -err >= (int) (sizeof (errors) / sizeof (errors[0]))) + return unknown; + + p = errors[-err].description; + if (!p) + p = unknown; + + return p; +} + + +/** + * ykpiv_strerror_name: + * @err: error code + * + * Convert return code to human readable string representing the error + * code symbol itself. For example, ykpiv_strerror_name(%YKPIV_OK) + * returns the string "YKPIV_OK". + * + * This string can be used to output a diagnostic message to the user. + * + * Return value: Returns a pointer to a statically allocated string + * containing a string version of the error code @err, or NULL if + * the error code is not known. + **/ +const char *ykpiv_strerror_name(ykpiv_rc err) { + if (-err < 0 || -err >= (int) (sizeof (errors) / sizeof (errors[0]))) + return NULL; + + return errors[-err].name; +} diff --git a/lib/internal.h b/lib/internal.h index d213456..605c470 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -30,9 +30,25 @@ #ifndef YKPIV_INTERNAL_H #define YKPIV_INTERNAL_H +#include + +#if BACKEND_PCSC +#if defined HAVE_PCSC_WINSCARD_H +# include +# include +#else +# include +#endif +#endif + struct ykpiv_state { - SCARDCONTEXT card; - SCARDHANDLE cardHandle; + SCARDCONTEXT context; + SCARDHANDLE card; + int verbose; +}; + +unsigned const char aid[] = { + 0xa0, 0x00, 0x00, 0x03, 0x08 }; #endif diff --git a/lib/ykpiv.c b/lib/ykpiv.c index 3467b0c..86cd9aa 100644 --- a/lib/ykpiv.c +++ b/lib/ykpiv.c @@ -29,30 +29,99 @@ #include #include - -#if BACKEND_PCSC -#if defined HAVE_PCSC_WINSCARD_H -# include -# include -#else -# include -#endif -#endif +#include #include "internal.h" #include "ykpiv.h" -int ykpiv_init(ykpiv_state **state) { - ykpiv_state *s = malloc(sizeof(ykpiv_state)); - if(s == NULL) { - return -1; - } - memset(s, 0, sizeof(ykpiv_state)); - *state = s; - return 0; +ykpiv_rc ykpiv_init(ykpiv_state **state, int verbose) { + ykpiv_state *s = malloc(sizeof(ykpiv_state)); + if(s == NULL) { + return YKPIV_MEMORY_ERROR; + } + memset(s, 0, sizeof(ykpiv_state)); + s->verbose = verbose; + *state = s; + return YKPIV_OK; } -int ykpiv_done(ykpiv_state *state) { - free(state); - return 0; +ykpiv_rc ykpiv_done(ykpiv_state *state) { + free(state); + return YKPIV_OK; +} + +ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) { + unsigned long num_readers = 0; + unsigned long active_protocol; + char reader_buf[1024]; + long rc; + char *reader_ptr; + + rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &state->context); + if (rc != SCARD_S_SUCCESS) { + if(state->verbose) { + fprintf (stderr, "error: SCardEstablishContext failed, rc=%08lx\n", rc); + } + return YKPIV_PCSC_ERROR; + } + + rc = SCardListReaders(state->context, NULL, NULL, &num_readers); + if (rc != SCARD_S_SUCCESS) { + if(state->verbose) { + fprintf (stderr, "error: SCardListReaders failed, rc=%08lx\n", rc); + } + SCardReleaseContext(state->context); + return YKPIV_PCSC_ERROR; + } + + if (num_readers > sizeof(reader_buf)) { + num_readers = sizeof(reader_buf); + } + + rc = SCardListReaders(state->context, NULL, reader_buf, &num_readers); + if (rc != SCARD_S_SUCCESS) + { + if(state->verbose) { + fprintf (stderr, "error: SCardListReaders failed, rc=%08lx\n", rc); + } + SCardReleaseContext(state->context); + return YKPIV_PCSC_ERROR; + } + + reader_ptr = reader_buf; + if(wanted) { + while(*reader_ptr != '\0') { + if(strstr(reader_ptr, wanted)) { + if(state->verbose) { + fprintf(stderr, "using reader '%s' matching '%s'.\n", reader_ptr, wanted); + } + break; + } else { + if(state->verbose) { + fprintf(stderr, "skipping reader '%s' since it doesn't match.\n", reader_ptr); + } + reader_ptr += strlen(reader_ptr) + 1; + } + } + } + if(*reader_ptr == '\0') { + if(state->verbose) { + fprintf(stderr, "error: no useable reader found.\n"); + } + SCardReleaseContext(state->context); + return YKPIV_PCSC_ERROR; + } + + rc = SCardConnect(state->context, reader_ptr, SCARD_SHARE_SHARED, + SCARD_PROTOCOL_T1, &state->card, &active_protocol); + if(rc != SCARD_S_SUCCESS) + { + if(state->verbose) { + fprintf(stderr, "error: SCardConnect failed, rc=%08lx\n", rc); + } + SCardReleaseContext(state->context); + return YKPIV_PCSC_ERROR; + } + + return YKPIV_OK; } diff --git a/lib/ykpiv.h b/lib/ykpiv.h index 6b74800..bb504ce 100644 --- a/lib/ykpiv.h +++ b/lib/ykpiv.h @@ -37,8 +37,20 @@ extern "C" typedef struct ykpiv_state ykpiv_state; - int ykpiv_init(ykpiv_state **state); - int ykpiv_done(ykpiv_state *state); + typedef enum { + YKPIV_OK = 0, + YKPIV_MEMORY_ERROR = -1, + YKPIV_PCSC_ERROR = -2, + } ykpiv_rc; + + const char *ykpiv_strerror(ykpiv_rc err); + const char *ykpiv_strerror_name(ykpiv_rc err); + + ykpiv_rc ykpiv_init(ykpiv_state **state, int verbose); + ykpiv_rc ykpiv_done(ykpiv_state *state); + ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted); + + #ifdef __cplusplus } diff --git a/lib/ykpiv.map b/lib/ykpiv.map index c9a2ae7..1207e64 100644 --- a/lib/ykpiv.map +++ b/lib/ykpiv.map @@ -28,6 +28,8 @@ YKPIV_0.1.0 { global: ykpiv_check_version; + ykpiv_strerror_name; + ykpiv_strerror; ykpiv_init; ykpiv_map;