diff --git a/MacOSX/build-package.in b/MacOSX/build-package.in index e0ab9fb3..6ec5f6ba 100755 --- a/MacOSX/build-package.in +++ b/MacOSX/build-package.in @@ -91,7 +91,23 @@ fi test -L OpenSC.tokend/build/opensc-src || ln -sf ${BUILDPATH}/src OpenSC.tokend/build/opensc-src # Build and copy OpenSC.tokend -xcodebuild -target OpenSC -configuration Deployment -project OpenSC.tokend/Tokend.xcodeproj install DSTROOT=${PWD}/target +xcodebuild -target OpenSC -configuration Deployment -project OpenSC.tokend/Tokend.xcodeproj install DSTROOT=${BUILDPATH}/target + +#if ! test -e $BUILDPATH/target/Library/Security/tokend/OpenSC.tokend/Contents/Resources/Applications/terminal-notifier.app; then + #if ! test -e terminal-notifier-1.7.1.zip; then + #curl -L https://github.com/julienXX/terminal-notifier/releases/download/1.7.1/terminal-notifier-1.7.1.zip > terminal-notifier-1.7.1.zip + #fi + #if ! test -e terminal-notifier-1.7.1; then + #unzip terminal-notifier-1.7.1.zip + #fi + #mkdir -p $BUILDPATH/target/Library/Security/tokend/OpenSC.tokend/Contents/Resources/Applications + #cp -r terminal-notifier-1.7.1/terminal-notifier.app $BUILDPATH/target/Library/Security/tokend/OpenSC.tokend/Contents/Resources/Applications +#fi + +if ! test -e NotificationProxy; then + git clone http://github.com/frankmorgner/NotificationProxy.git +fi +xcodebuild -target NotificationProxy -configuration Release -project NotificationProxy/NotificationProxy.xcodeproj install DSTROOT=$BUILDPATH/target/Library/Security/tokend/OpenSC.tokend/Contents/Resources/ # Prepare target root # The "UnInstaller" diff --git a/configure.ac b/configure.ac index ba45c210..e0bcf539 100644 --- a/configure.ac +++ b/configure.ac @@ -245,6 +245,13 @@ AC_ARG_ENABLE( [enable_dnie_ui="no"] ) +AC_ARG_ENABLE( + [notify], + [AS_HELP_STRING([--enable-notify],[enable notifications @<:@detect@:>@])], + , + [enable_notify="detect"] +) + AC_ARG_ENABLE( [werror-declaration-after-statement], [AS_HELP_STRING([--disable-werror-declaration-after-statement],[disable -Werror 'declaration-after-statement' @<:@enabled@:>@])], @@ -443,7 +450,7 @@ if test "${enable_dnie_ui}" = "yes"; then AC_DEFINE([ENABLE_DNIE_UI], [1], [Enable the use of external user interface program to request DNIe user pin]) case "${host}" in - *-apple-*) + *-*-darwin) if test "${enable_dnie_ui}" = "yes"; then LDFLAGS="${LDFLAGS} -framework Carbon" fi @@ -451,6 +458,37 @@ if test "${enable_dnie_ui}" = "yes"; then esac fi +case "${host}" in + *-*-darwin*) + have_notify="yes" + ;; + *) + ;; +esac + +case "${enable_notify}" in + no) + have_notify="no" + ;; + detect) + if test "${have_notify}" = "yes"; then + enable_notify="yes" + else + enable_notify="no" + fi + ;; +esac + +if test "${enable_notify}" = "yes"; then + if test "${have_notify}" = "yes"; then + AC_DEFINE([ENABLE_NOTIFY], [1], [Use notification libraries and header files]) + OPTIONAL_NOTIFY_CFLAGS="${NOTIFY_CFLAGS}" + OPTIONAL_NOTIFY_LIBS="${NOTIFY_LIBS}" + else + AC_MSG_ERROR([notification linkage required, but no notification provider was found]) + fi +fi + AC_ARG_VAR([ZLIB_CFLAGS], [C compiler flags for zlib]) AC_ARG_VAR([ZLIB_LIBS], [linker flags for zlib]) if test -z "${ZLIB_LIBS}"; then @@ -914,6 +952,8 @@ AC_SUBST([DEFAULT_SM_MODULE_PATH]) AC_SUBST([DEBUG_FILE]) AC_SUBST([PROFILE_DIR]) AC_SUBST([PROFILE_DIR_DEFAULT]) +AC_SUBST([OPTIONAL_NOTIFY_CFLAGS]) +AC_SUBST([OPTIONAL_NOTIFY_LIBS]) AM_CONDITIONAL([ENABLE_MAN], [test "${enable_man}" = "yes"]) AM_CONDITIONAL([ENABLE_THREAD_LOCKING], [test "${enable_thread_locking}" = "yes"]) @@ -956,6 +996,7 @@ AC_CONFIG_FILES([ etc/Makefile src/Makefile src/common/Makefile + src/ui/Makefile src/libopensc/Makefile src/sm/Makefile src/pkcs11/Makefile @@ -1021,6 +1062,7 @@ minidriver support: ${enable_minidriver} SM support: ${enable_sm} SM default module: ${DEFAULT_SM_MODULE} DNIe UI support: ${enable_dnie_ui} +Notification support: ${enable_notify} Debug file: ${DEBUG_FILE} PC/SC default provider: ${DEFAULT_PCSC_PROVIDER} @@ -1045,6 +1087,8 @@ OPENCT_CFLAGS: ${OPENCT_CFLAGS} OPENCT_LIBS: ${OPENCT_LIBS} PCSC_CFLAGS: ${PCSC_CFLAGS} CRYPTOTOKENKIT_CFLAGS: ${CRYPTOTOKENKIT_CFLAGS} +NOTIFY_CFLAGS: ${NOTIFY_CFLAGS} +NOTIFY_LIBS: ${NOTIFY_LIBS} EOF diff --git a/src/Makefile.am b/src/Makefile.am index ab1dd1a3..64d05d93 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,7 +2,7 @@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak # Order IS important -SUBDIRS = common scconf pkcs15init sm \ +SUBDIRS = common scconf ui pkcs15init sm \ libopensc pkcs11 tools tests minidriver if ENABLE_SM diff --git a/src/Makefile.mak b/src/Makefile.mak index 52be9224..f6609f77 100644 --- a/src/Makefile.mak +++ b/src/Makefile.mak @@ -1,6 +1,6 @@ TOPDIR = .. -SUBDIRS = common scconf sm pkcs15init \ +SUBDIRS = common scconf ui sm pkcs15init \ libopensc pkcs11 tools tests default: all diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index 6dc37c30..c1172eb2 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -78,6 +78,8 @@ libopensc_la_LIBADD = $(OPENPACE_LIBS) $(OPTIONAL_OPENSSL_LIBS) \ $(top_builddir)/src/pkcs15init/libpkcs15init.la \ $(top_builddir)/src/scconf/libscconf.la \ $(top_builddir)/src/common/libscdl.la \ + $(top_builddir)/src/ui/libnotify.la \ + $(top_builddir)/src/ui/libstrings.la \ $(top_builddir)/src/sm/libsmeac.la \ $(top_builddir)/src/common/libcompat.la if WIN32 diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak index 00c29174..f0ee0838 100644 --- a/src/libopensc/Makefile.mak +++ b/src/libopensc/Makefile.mak @@ -41,6 +41,8 @@ OBJECTS = \ LIBS = $(TOPDIR)\src\scconf\scconf.lib \ $(TOPDIR)\src\common\common.lib \ $(TOPDIR)\src\common\libscdl.lib \ + $(TOPDIR)\src\ui\strings.lib \ + $(TOPDIR)\src\ui\notify.lib \ $(TOPDIR)\src\sm\libsmiso.lib \ $(TOPDIR)\src\sm\libsmeac.lib \ $(TOPDIR)\src\pkcs15init\pkcs15init.lib diff --git a/src/libopensc/libopensc.exports b/src/libopensc/libopensc.exports index 7931a268..c402ffb6 100644 --- a/src/libopensc/libopensc.exports +++ b/src/libopensc/libopensc.exports @@ -363,3 +363,8 @@ escape_pace_output_to_buf escape_buf_to_pace_output escape_pace_capabilities_to_buf escape_buf_to_pace_capabilities +ui_get_str +sc_notify_init +sc_notify_close +sc_notify +sc_notify_id diff --git a/src/libopensc/pkcs15-pin.c b/src/libopensc/pkcs15-pin.c index 1c7df891..f5980278 100644 --- a/src/libopensc/pkcs15-pin.c +++ b/src/libopensc/pkcs15-pin.c @@ -30,6 +30,7 @@ #include "internal.h" #include "asn1.h" #include "pkcs15.h" +#include "ui/notify.h" int _sc_pkcs15_verify_pin(struct sc_pkcs15_card *, struct sc_pkcs15_object *, const unsigned char *, size_t); @@ -358,12 +359,14 @@ int sc_pkcs15_verify_pin_with_session_pin(struct sc_pkcs15_card *p15card, "PIN(type:%X; method:%X; value(%p:%"SC_FORMAT_LEN_SIZE_T"u)", auth_info->auth_type, auth_info->auth_method, pincode, pinlen); - - if (pinlen > SC_MAX_PIN_SIZE) - LOG_TEST_RET(ctx, SC_ERROR_INVALID_PIN_LENGTH, "Invalid PIN size"); - card = p15card->card; + if (pinlen > SC_MAX_PIN_SIZE) { + sc_notify_id(card->ctx, &card->reader->atr, p15card, + NOTIFY_PIN_BAD); + LOG_TEST_RET(ctx, SC_ERROR_INVALID_PIN_LENGTH, "Invalid PIN size"); + } + /* Initialize arguments */ memset(&data, 0, sizeof(data)); data.pin_type = auth_info->auth_method; @@ -408,10 +411,10 @@ int sc_pkcs15_verify_pin_with_session_pin(struct sc_pkcs15_card *p15card, data.pin_reference = skey_info->key_reference; } - if((p15card->card->reader->capabilities & SC_READER_CAP_PIN_PAD - || p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH) - && !pinlen) { - data.flags |= SC_PIN_CMD_USE_PINPAD; + if ((p15card->card->reader->capabilities & SC_READER_CAP_PIN_PAD + || p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH)) { + if (!pincode && !pinlen) + data.flags |= SC_PIN_CMD_USE_PINPAD; if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) data.pin1.prompt = "Please enter SO PIN"; @@ -450,10 +453,19 @@ int sc_pkcs15_verify_pin_with_session_pin(struct sc_pkcs15_card *p15card, *sessionpinlen = data.pin2.len; } } else { + sc_notify_id(card->ctx, &card->reader->atr, p15card, + NOTIFY_PIN_BAD); if (data.cmd == SC_PIN_CMD_GET_SESSION_PIN) { *sessionpinlen = 0; } } + + if (auth_info->auth_type == SC_PKCS15_PIN_AUTH_TYPE_PIN + && auth_info->auth_method != SC_AC_SESSION) { + sc_notify_id(card->ctx, &card->reader->atr, p15card, + r == SC_SUCCESS ? NOTIFY_PIN_GOOD : NOTIFY_PIN_BAD); + } + out: sc_unlock(card); LOG_FUNC_RETURN(ctx, r); diff --git a/src/pkcs11/framework-pkcs15.c b/src/pkcs11/framework-pkcs15.c index 5b3cb32e..2f0ddc2b 100644 --- a/src/pkcs11/framework-pkcs15.c +++ b/src/pkcs11/framework-pkcs15.c @@ -25,6 +25,7 @@ #include "libopensc/internal.h" #include "libopensc/asn1.h" #include "libopensc/cardctl.h" +#include "ui/notify.h" #include "common/compat_strnlen.h" #ifdef ENABLE_OPENSSL @@ -329,6 +330,12 @@ pkcs15_bind(struct sc_pkcs11_card *p11card, struct sc_app_info *app_info) } } + if (idx == 0) { + /* send a notification only for the first application that's bound */ + sc_notify_id(p11card->card->ctx, &p11card->reader->atr, fw_data->p15_card, + NOTIFY_CARD_INSERTED); + } + return CKR_OK; } @@ -356,8 +363,20 @@ pkcs15_unbind(struct sc_pkcs11_card *p11card) unlock_card(fw_data); - if (fw_data->p15_card) + if (fw_data->p15_card) { + if (idx == 0) { + int rc = sc_detect_card_presence(fw_data->p15_card->card->reader); + if (rc <= 0 || rc & SC_READER_CARD_CHANGED) { + /* send a notification only if the card was removed/changed + * and only for the first application that's unbound */ + sc_notify_id(fw_data->p15_card->card->ctx, + &fw_data->p15_card->card->reader->atr, + fw_data->p15_card, + NOTIFY_CARD_REMOVED); + } + } rv = sc_pkcs15_unbind(fw_data->p15_card); + } fw_data->p15_card = NULL; free(fw_data); diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am index 471ed5a8..9eb827b1 100644 --- a/src/tools/Makefile.am +++ b/src/tools/Makefile.am @@ -19,7 +19,7 @@ EXTRA_DIST = Makefile.mak versioninfo-tools.rc.in npa-tool.ggo.in npa-tool.1 noinst_HEADERS = util.h fread_to_eof.h noinst_PROGRAMS = sceac-example bin_PROGRAMS = opensc-tool opensc-explorer pkcs15-tool pkcs15-crypt \ - pkcs11-tool cardos-tool eidenv openpgp-tool iasecc-tool + pkcs11-tool cardos-tool eidenv openpgp-tool iasecc-tool opensc-notify if ENABLE_OPENSSL bin_PROGRAMS += cryptoflex-tool pkcs15-init netkey-tool piv-tool \ westcos-tool sc-hsm-tool dnie-tool gids-tool npa-tool @@ -46,6 +46,7 @@ piv_tool_SOURCES = piv-tool.c util.c piv_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) opensc_explorer_SOURCES = opensc-explorer.c util.c opensc_explorer_LDADD = $(OPTIONAL_READLINE_LIBS) +opensc_notify_SOURCES = opensc-notify.c pkcs15_tool_SOURCES = pkcs15-tool.c util.c pkcs15_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) pkcs11_tool_SOURCES = pkcs11-tool.c util.c diff --git a/src/tools/Makefile.mak b/src/tools/Makefile.mak index d03c52c7..2032c94a 100644 --- a/src/tools/Makefile.mak +++ b/src/tools/Makefile.mak @@ -6,6 +6,7 @@ default: all TARGETS = opensc-tool.exe opensc-explorer.exe pkcs15-tool.exe pkcs15-crypt.exe \ pkcs11-tool.exe cardos-tool.exe eidenv.exe openpgp-tool.exe iasecc-tool.exe \ + opensc-notify.exe \ $(PROGRAMS_OPENSSL) OBJECTS = util.obj npa-tool-cmdline.obj fread_to_eof.obj versioninfo-tools.res diff --git a/src/tools/opensc-notify.c b/src/tools/opensc-notify.c new file mode 100644 index 00000000..c5b16c86 --- /dev/null +++ b/src/tools/opensc-notify.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 Frank Morgner + * + * This file is part of OpenSC. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ui/notify.h" +#include + +int +main (int argc, char **argv) +{ + const char *title = NULL, *text = NULL; + switch (argc) { + case 3: + text = argv[2]; + /* fall through */ + case 2: + text = argv[1]; + /* fall through */ + case 1: + break; + + default: + fprintf(stderr, "Usage: opensc-notify [title [text]]"); + return 1; + } + sc_notify_init(); + sc_notify(title, text); + sc_notify_close(); + + return 0; +} diff --git a/src/ui/Makefile.am b/src/ui/Makefile.am new file mode 100644 index 00000000..f64f24fd --- /dev/null +++ b/src/ui/Makefile.am @@ -0,0 +1,16 @@ +include $(top_srcdir)/win32/ltrc.inc + +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in +EXTRA_DIST = Makefile.mak + +noinst_LTLIBRARIES = libstrings.la libnotify.la +noinst_HEADERS = strings.h notify.h + +AM_CPPFLAGS = -I$(top_srcdir)/src +AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_NOTIFY_CFLAGS) +AM_OBJCFLAGS = $(AM_CFLAGS) + +libstrings_la_SOURCES = strings.c + +libnotify_la_SOURCES = notify.c +libnotify_la_LIBADD = $(OPTIONAL_NOTIFY_LIBS) diff --git a/src/ui/Makefile.mak b/src/ui/Makefile.mak new file mode 100644 index 00000000..f1a37540 --- /dev/null +++ b/src/ui/Makefile.mak @@ -0,0 +1,17 @@ +TOPDIR = ..\.. + +TARGET = strings.lib +OBJECTS = strings.obj + +TARGET2 = notify.lib +OBJECTS2 = notify.obj + +all: $(TARGET) $(TARGET2) + +!INCLUDE $(TOPDIR)\win32\Make.rules.mak + +$(TARGET): $(OBJECTS) + lib $(LIBFLAGS) /out:$(TARGET) $(OBJECTS) + +$(TARGET2): $(OBJECTS2) + lib $(LIBFLAGS) /out:$(TARGET2) $(OBJECTS2) diff --git a/src/ui/notify.c b/src/ui/notify.c new file mode 100644 index 00000000..b55432dc --- /dev/null +++ b/src/ui/notify.c @@ -0,0 +1,176 @@ +/* + * notify.c: Notification implementation + * + * Copyright (C) 2017 Frank Morgner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include "notify.h" + +#if defined(ENABLE_NOTIFY) && defined(__APPLE__) + +#include "libopensc/log.h" +#include +#include +#include +#include +#include + +static pid_t child = -1; + +void sc_notify_init(void) +{ +} + +void sc_notify_close(void) +{ + if (child > 0) { + int i, status; + for (i = 0; child != waitpid(child, &status, WNOHANG); i++) { + switch (i) { + case 0: + kill(child, SIGKILL); + break; + case 1: + kill(child, SIGTERM); + break; + default: + /* SIGTERM was our last resort */ + return; + } + usleep(100); + } + child = -1; + } +} + +static void notify_proxy(struct sc_context *ctx, + const char *title, const char* subtitle, + const char *text, const char *icon, const char *sound, + const char *group) +{ + /* terminal-notifier does not reliably activate keychain when clicked on + * the notification + * (https://github.com/julienXX/terminal-notifier/issues/196), that's why + * we're including NotificationProxy which has similar features */ + const char notificationproxy[] = "/Library/Security/tokend/OpenSC.tokend/Contents/Resources/Applications/NotificationProxy.app/Contents/MacOS/NotificationProxy"; + + if (child > 0) { + int status; + if (0 == waitpid(child, &status, WNOHANG)) { + kill(child, SIGKILL); + usleep(100); + if (0 == waitpid(child, &status, WNOHANG)) { + sc_log(ctx, "Can't kill %ld, skipping current notification", (long) child); + return; + } + } + } + + child = fork(); + switch (child) { + case 0: + /* child process */ + + /* for some reason the user _tokend can call brew's installation of + * terminal-notifier, but it cannot call `/usr/bin/open` with + * NotificationProxy.app that we're shipping... However, while + * `sudo -u _tokend /usr/local/bin/terminal-notifier -title test` + * works in the terminal, it hangs when executed from the tokend + * process. For now, we try to deliver the notification anyway + * making sure that we are waiting for only one forked process. */ + if (0 > execl(notificationproxy, notificationproxy, + title ? title : "", + subtitle ? subtitle : "", + text ? text : "", + icon ? icon : "", + group ? group : "", + sound ? sound : "", + (char *) NULL)) { + perror("exec failed"); + exit(0); + } + break; + case -1: + sc_log(ctx, "failed to fork for notification"); + break; + default: + if (ctx) { + sc_log(ctx, "Created %ld for notification:", (long) child); + sc_log(ctx, "%s %s %s %s %s %s %s", notificationproxy, + title ? title : "", + subtitle ? subtitle : "", + text ? text : "", + icon ? icon : "", + group ? group : "", + sound ? sound : ""); + } + break; + } +} + +void sc_notify(const char *title, const char *text) +{ + notify_proxy(NULL, title, NULL, text, NULL, NULL, NULL); +} + +void sc_notify_id(struct sc_context *ctx, struct sc_atr *atr, + struct sc_pkcs15_card *p15card, enum ui_str id) +{ + const char *title, *text, *icon, *group; + title = ui_get_str(ctx, atr, p15card, id); + text = ui_get_str(ctx, atr, p15card, id+1); + + if (p15card && p15card->card && p15card->card->reader) { + group = p15card->card->reader->name; + } else { + group = ctx ? ctx->app_name : NULL; + } + + switch (id) { + case NOTIFY_CARD_INSERTED: + icon = "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/VCard.icns"; + break; + case NOTIFY_CARD_REMOVED: + icon = "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/EjectMediaIcon.icns"; + break; + case NOTIFY_PIN_GOOD: + icon = "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/UnlockedIcon.icns"; + break; + case NOTIFY_PIN_BAD: + icon = "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/LockedIcon.icns"; + break; + default: + icon = NULL; + break; + } + + notify_proxy(ctx, title, NULL, text, icon, NULL, group); +} + +#else + +void sc_notify_init(void) {} +void sc_notify_close(void) {} +void sc_notify(const char *title, const char *text) {} +void sc_notify_id(struct sc_context *ctx, struct sc_atr *atr, + struct sc_pkcs15_card *p15card, enum ui_str id) {} + +#endif diff --git a/src/ui/notify.h b/src/ui/notify.h new file mode 100644 index 00000000..d290cb1b --- /dev/null +++ b/src/ui/notify.h @@ -0,0 +1,40 @@ +/* + * notify.h: OpenSC library header file + * + * Copyright (C) 2017 Frank Morgner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NOTIFY_H +#define _NOTIFY_H + +#include "strings.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void sc_notify_init(void); +void sc_notify_close(void); +void sc_notify(const char *title, const char *text); +void sc_notify_id(struct sc_context *ctx, struct sc_atr *atr, + struct sc_pkcs15_card *p15card, enum ui_str id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/ui/strings.c b/src/ui/strings.c new file mode 100644 index 00000000..383eb7ff --- /dev/null +++ b/src/ui/strings.c @@ -0,0 +1,313 @@ +/* + * strings.c: Implementation of default UI strings + * + * Copyright (C) 2017 Frank Morgner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "libopensc/internal.h" +#include "strings.h" +#include +#include + +enum ui_langs { + EN, + DE, +}; + +static const char *get_inserted_text(struct sc_pkcs15_card *p15card, struct sc_atr *atr) +{ + static char text[3*SC_MAX_ATR_SIZE] = {0}; + const char prefix[] = "ATR: "; + + if (p15card && p15card->card && p15card->card->name) { + return p15card->card->name; + } + + if (!atr) + return NULL; + + strcpy(text, prefix); + sc_bin_to_hex(atr->value, atr->len, text + (sizeof prefix) - 1, + sizeof(text) - (sizeof prefix) - 1, ':'); + + return text; +} + +static const char *get_removed_text(struct sc_pkcs15_card *p15card) +{ + if (p15card && p15card->card && p15card->card->reader + && p15card->card->reader->name) { + return p15card->card->reader->name; + } + + return NULL; +} + +static const char *ui_get_config_str(struct sc_context *ctx, + struct sc_atr *atr, const char *flag_name, const char *ret_default) +{ + const char *ret = ret_default; + + scconf_block *atrblock = _sc_match_atr_block(ctx, NULL, atr); + + if (atrblock) + ret = scconf_get_str(atrblock, flag_name, ret_default); + + return ret; +} + +static int find_lang_str(const char *str, enum ui_langs *lang) +{ + if (str) { + if (0 == strncmp(str, "de", 2)) { + if (lang) { + *lang = DE; + } + return 1; + } else if (0 == strncmp(str, "en", 2)) { + if (lang) { + *lang = EN; + } + return 1; + } + } + + return 0; +} + +const char *ui_get_str(struct sc_context *ctx, struct sc_atr *atr, + struct sc_pkcs15_card *p15card, enum ui_str id) +{ + enum ui_langs lang = EN; + const char *str, *option; + + /* load option strings */ + switch (id) { + case MD_PINPAD_DLG_TITLE: + option = "md_pinpad_dlg_title"; + break; + case MD_PINPAD_DLG_MAIN: + option = "md_pinpad_dlg_main"; + break; + case MD_PINPAD_DLG_CONTENT_USER: + option = "md_pinpad_dlg_content_user"; + break; + case MD_PINPAD_DLG_CONTENT_ADMIN: + option = "md_pinpad_dlg_content_admin"; + break; + case MD_PINPAD_DLG_CONTENT_CANCEL: + option = "md_pinpad_dlg_content_cancel"; + break; + case MD_PINPAD_DLG_EXPANDED: + option = "md_pinpad_dlg_expanded"; + break; + case MD_PINPAD_DLG_EXPANDED_CANCEL: + option = "md_pinpad_dlg_expanded_cancel"; + break; + case MD_PINPAD_DLG_ICON: + option = "md_pinpad_dlg_icon"; + break; + case NOTIFY_CARD_INSERTED: + option = "notify_card_inserted"; + break; + case NOTIFY_CARD_INSERTED_TEXT: + option = "notify_card_inserted_text"; + break; + case NOTIFY_CARD_REMOVED: + option = "notify_card_removed"; + break; + case NOTIFY_CARD_REMOVED_TEXT: + option = "notify_card_removed_text"; + break; + case NOTIFY_PIN_GOOD: + option = "notify_pin_good"; + break; + case NOTIFY_PIN_GOOD_TEXT: + option = "notify_pin_good_text"; + break; + case NOTIFY_PIN_BAD: + option = "notify_pin_bad"; + break; + case NOTIFY_PIN_BAD_TEXT: + option = "notify_pin_bad_text"; + break; + default: + option = NULL; + break; + } + + /* load language */ + /* card's language supersedes system's language */ + if (!p15card || !p15card->tokeninfo + || !find_lang_str(p15card->tokeninfo->preferred_language, &lang)) { +#ifdef _WIN32 + LANGID langid = GetUserDefaultUILanguage(); + if (langid & LANG_GERMAN) { + lang = DE; + } +#else + /* LANGUAGE supersedes locale */ + if (!find_lang_str(getenv("LANGUAGE"), &lang)) { + /* XXX Should we use LC_MESSAGES instead? */ + find_lang_str(setlocale(LC_ALL, ""), &lang); + } +#endif + } + + /* load default strings */ + switch (lang) { + case DE: + switch (id) { + case MD_PINPAD_DLG_TITLE: + str = "Windows-Sicherheit"; + break; + case MD_PINPAD_DLG_MAIN: + str = "OpenSC Smartcard-Anbieter"; + break; + case MD_PINPAD_DLG_CONTENT_USER: + str = "Bitte geben Sie Ihre PIN für die digitale Signatur auf dem PIN-Pad ein."; + break; + case MD_PINPAD_DLG_CONTENT_ADMIN: + str = "Bitte geben Sie Ihre PIN zum Entsperren der Nutzer-PIN auf dem PIN-Pad ein."; + break; + case MD_PINPAD_DLG_CONTENT_CANCEL: + str = "Nutzen Sie das PIN-Pad, um den Vorgang abzubrechen."; + break; + case MD_PINPAD_DLG_EXPANDED: + str = "Dieses Fenster wird automatisch geschlossen, wenn die PIN am PIN-Pad eingegeben wurde (Timeout typischerweise nach 30 Sekunden)."; + break; + case MD_PINPAD_DLG_EXPANDED_CANCEL: + str = "Einige Kartenleser unterstützen das Abbrechen ausschließlich am PIN-Pad. Drücken Sie Cancel (Abbruch) oder entfernen Sie die Karte."; + break; + case NOTIFY_CARD_INSERTED: + if (p15card) { + str = "Smartcard kann jetzt verwendet werden"; + } else { + str = "Smartcard erkannt"; + } + break; + case NOTIFY_CARD_INSERTED_TEXT: + str = get_inserted_text(p15card, atr); + break; + case NOTIFY_CARD_REMOVED: + str = "Smartcard entfernt"; + break; + case NOTIFY_CARD_REMOVED_TEXT: + str = get_removed_text(p15card); + break; + case NOTIFY_PIN_GOOD: + str = "PIN verifiziert"; + break; + case NOTIFY_PIN_GOOD_TEXT: + str = "Smartcard ist entsperrt"; + break; + case NOTIFY_PIN_BAD: + str = "PIN nicht verifiziert"; + break; + case NOTIFY_PIN_BAD_TEXT: + str = "Smartcard ist gesperrt"; + break; + + case MD_PINPAD_DLG_CONTROL_COLLAPSED: + /* fall through */ + case MD_PINPAD_DLG_CONTROL_EXPANDED: + str = "Weitere Informationen"; + break; + case MD_PINPAD_DLG_CANCEL: + str = "Abbrechen"; + break; + default: + str = NULL; + break; + } + break; + case EN: + /* fall through */ + default: + switch (id) { + case MD_PINPAD_DLG_TITLE: + str = "Windows Security"; + break; + case MD_PINPAD_DLG_MAIN: + str = "OpenSC Smart Card Provider"; + break; + case MD_PINPAD_DLG_CONTENT_USER: + str = "Please enter your digital signature PIN on the PIN pad."; + break; + case MD_PINPAD_DLG_CONTENT_ADMIN: + str = "Please enter your PIN to unblock the user PIN on the PIN pad."; + break; + case MD_PINPAD_DLG_CONTENT_CANCEL: + str = "Use the PIN pad to cancel the operation."; + break; + case MD_PINPAD_DLG_EXPANDED: + str = "This window will be closed automatically after the PIN has been submitted on the PIN pad (timeout typically after 30 seconds)."; + break; + case MD_PINPAD_DLG_EXPANDED_CANCEL: + str = "Some readers only support canceling the operation on the PIN pad. Press Cancel or remove the card."; + break; + case NOTIFY_CARD_INSERTED: + if (p15card) { + str = "Smart card is ready to use"; + } else { + str = "Smart card detected"; + } + break; + case NOTIFY_CARD_INSERTED_TEXT: + str = get_inserted_text(p15card, atr); + break; + case NOTIFY_CARD_REMOVED: + str = "Smart card removed"; + break; + case NOTIFY_CARD_REMOVED_TEXT: + str = get_removed_text(p15card); + break; + case NOTIFY_PIN_GOOD: + str = "PIN verified"; + break; + case NOTIFY_PIN_GOOD_TEXT: + str = "Smart card is unlocked"; + break; + case NOTIFY_PIN_BAD: + str = "PIN not verified"; + break; + case NOTIFY_PIN_BAD_TEXT: + str = "Smart card is locked"; + break; + case MD_PINPAD_DLG_CONTROL_COLLAPSED: + /* fall through */ + case MD_PINPAD_DLG_CONTROL_EXPANDED: + str = "Click here for more information"; + break; + case MD_PINPAD_DLG_CANCEL: + str = "Cancel"; + break; + default: + str = NULL; + break; + } + break; + } + + /* user's strings supersede default strings */ + if (option != NULL) { + /* overwrite str with the user's choice */ + str = ui_get_config_str(ctx, atr, option, str); + } + + return str; +} diff --git a/src/ui/strings.h b/src/ui/strings.h new file mode 100644 index 00000000..6369d8bc --- /dev/null +++ b/src/ui/strings.h @@ -0,0 +1,59 @@ +/* + * strings.c: default UI strings + * + * Copyright (C) 2017 Frank Morgner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _STRINGS_H +#define _STRINGS_H + +#include "libopensc/pkcs15.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum ui_str { + MD_PINPAD_DLG_TITLE, + MD_PINPAD_DLG_MAIN, + MD_PINPAD_DLG_CONTENT_USER, + MD_PINPAD_DLG_CONTENT_ADMIN, + MD_PINPAD_DLG_CONTENT_CANCEL, + MD_PINPAD_DLG_EXPANDED, + MD_PINPAD_DLG_EXPANDED_CANCEL, + MD_PINPAD_DLG_CONTROL_COLLAPSED, + MD_PINPAD_DLG_CONTROL_EXPANDED, + MD_PINPAD_DLG_ICON, + MD_PINPAD_DLG_CANCEL, + NOTIFY_CARD_INSERTED, + NOTIFY_CARD_INSERTED_TEXT, + NOTIFY_CARD_REMOVED, + NOTIFY_CARD_REMOVED_TEXT, + NOTIFY_PIN_GOOD, + NOTIFY_PIN_GOOD_TEXT, + NOTIFY_PIN_BAD, + NOTIFY_PIN_BAD_TEXT, +}; + +const char *ui_get_str(struct sc_context *ctx, struct sc_atr *atr, + struct sc_pkcs15_card *p15card, enum ui_str id); + +#ifdef __cplusplus +} +#endif + +#endif