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;