aboutsummaryrefslogtreecommitdiffstats
path: root/scd
diff options
context:
space:
mode:
Diffstat (limited to 'scd')
-rw-r--r--scd/ChangeLog14
-rw-r--r--scd/app-common.h2
-rw-r--r--scd/app.c127
-rw-r--r--scd/command.c49
4 files changed, 173 insertions, 19 deletions
diff --git a/scd/ChangeLog b/scd/ChangeLog
index 5dab2019c..3b850a293 100644
--- a/scd/ChangeLog
+++ b/scd/ChangeLog
@@ -1,3 +1,17 @@
+2006-02-09 Werner Koch <[email protected]>
+
+ * app.c (release_application): Factored code out to ..
+ (deallocate_app): new function.
+ (select_application): Introduce new saved application stuff.
+ (application_notify_card_removed): New.
+ * command.c (update_card_removed): Call it.
+
+ * app.c (check_application_conflict): New.
+ * command.c (open_card): Use it here.
+ (cmd_restart): New command.
+
+ * command.c (cmd_lock): Fixed --wait option to actually terminate.
+
2006-02-08 Werner Koch <[email protected]>
* ccid-driver.c (ccid_get_atr): Read Parameter and select T=1
diff --git a/scd/app-common.h b/scd/app-common.h
index 94087f221..d294a5c25 100644
--- a/scd/app-common.h
+++ b/scd/app-common.h
@@ -129,6 +129,8 @@ size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);
/*-- app.c --*/
void app_dump_state (void);
+void application_notify_card_removed (int slot);
+gpg_error_t check_application_conflict (ctrl_t ctrl, const char *name);
gpg_error_t select_application (ctrl_t ctrl, int slot, const char *name,
app_t *r_app);
void release_application (app_t app);
diff --git a/scd/app.c b/scd/app.c
index f27b400b1..fad45ed85 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -40,9 +40,15 @@ static struct
int initialized;
pth_mutex_t lock;
app_t app; /* Application context in use or NULL. */
+ app_t last_app; /* Last application object used as this slot or NULL. */
} lock_table[10];
+
+static void deallocate_app (app_t app);
+
+
+
/* Lock the reader SLOT. This function shall be used right before
calling any of the actual application functions to serialize access
to the reader. We do this always even if the reader is not
@@ -68,6 +74,7 @@ lock_reader (int slot)
}
lock_table[slot].initialized = 1;
lock_table[slot].app = NULL;
+ lock_table[slot].last_app = NULL;
}
if (!pth_mutex_acquire (&lock_table[slot].lock, 0, NULL))
@@ -121,13 +128,21 @@ app_dump_state (void)
log_info ("app_dump_state: slot=%d lock=", slot);
dump_mutex_state (&lock_table[slot].lock);
if (lock_table[slot].app)
- log_printf (" app=%p type=`%s'",
- lock_table[slot].app, lock_table[slot].app->apptype);
+ {
+ log_printf (" app=%p", lock_table[slot].app);
+ if (lock_table[slot].app->apptype)
+ log_printf (" type=`%s'", lock_table[slot].app->apptype);
+ }
+ if (lock_table[slot].last_app)
+ {
+ log_printf (" lastapp=%p", lock_table[slot].last_app);
+ if (lock_table[slot].last_app->apptype)
+ log_printf (" type=`%s'", lock_table[slot].last_app->apptype);
+ }
log_printf ("\n");
}
}
-
/* Check wether the application NAME is allowed. This does not mean
we have support for it though. */
static int
@@ -142,6 +157,46 @@ is_app_allowed (const char *name)
}
+/* This may be called to tell this module about a removed card. */
+void
+application_notify_card_removed (int slot)
+{
+ if (slot < 0 || slot >= DIM (lock_table))
+ return;
+
+ /* Deallocate a saved application for that slot, so that we won't
+ try to reuse it. */
+ if (lock_table[slot].initialized && lock_table[slot].last_app)
+ {
+ app_t app = lock_table[slot].last_app;
+
+ lock_table[slot].last_app = NULL;
+ deallocate_app (app);
+ }
+}
+
+
+/* This fucntion is used by the serialno command to check for an
+ application conflict which may appear if the serialno command is
+ used to request a specific application and the connection has
+ already done a select_application. */
+gpg_error_t
+check_application_conflict (ctrl_t ctrl, const char *name)
+{
+ int slot = ctrl->reader_slot;
+ app_t app;
+
+ if (slot < 0 || slot >= DIM (lock_table))
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ app = lock_table[slot].initialized ? lock_table[slot].app : NULL;
+ if (app && app->apptype && name)
+ if ( ascii_strcasecmp (app->apptype, name))
+ return gpg_error (GPG_ERR_CONFLICT);
+ return 0;
+}
+
+
/* If called with NAME as NULL, select the best fitting application
and return a context; otherwise select the application with NAME
and return a context. SLOT identifies the reader device. Returns
@@ -173,6 +228,32 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
return gpg_error (GPG_ERR_CONFLICT);
}
+ /* If we don't have an app, check whether we have a saved
+ application for that slot. This is useful so that a card does
+ not get reset even if only one session is using the card - so the
+ PIN cache and other cached data are preserved. */
+ if (!app && lock_table[slot].initialized && lock_table[slot].last_app)
+ {
+ app = lock_table[slot].last_app;
+ if (!name || (app->apptype && !ascii_strcasecmp (app->apptype, name)) )
+ {
+ /* Yes, we can reuse this application - either the caller
+ requested an unspecific one or the requested one matches
+ the saved one. */
+ lock_table[slot].app = app;
+ lock_table[slot].last_app = NULL;
+ }
+ else
+ {
+ /* No, this saved application can't be used - deallocate it. */
+ lock_table[slot].last_app = NULL;
+ deallocate_app (app);
+ app = NULL;
+ }
+ }
+
+ /* If we can reuse an application, bump the reference count and
+ return it. */
if (app)
{
if (app->slot != slot)
@@ -183,6 +264,7 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
return 0; /* Okay: We share that one. */
}
+ /* Need to allocate a new one. */
app = xtrycalloc (1, sizeof *app);
if (!app)
{
@@ -281,9 +363,25 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
}
+/* Deallocate the application. */
+static void
+deallocate_app (app_t app)
+{
+ if (app->fnc.deinit)
+ {
+ app->fnc.deinit (app);
+ app->fnc.deinit = NULL;
+ }
+
+ xfree (app->serialno);
+ xfree (app);
+}
+
/* Free the resources associated with the application APP. APP is
allowed to be NULL in which case this is a no-op. Note that we are
- using reference counting to track the users of the application. */
+ using reference counting to track the users of the application and
+ actually deferiing the deallcoation to allow for a later resuse by
+ a new connection. */
void
release_application (app_t app)
{
@@ -297,20 +395,19 @@ release_application (app_t app)
if (--app->ref_count)
return;
- /* Clear the reference to the application from the lock table. */
+ /* Move the reference to the application in the lock table. */
for (slot = 0; slot < DIM (lock_table); slot++)
if (lock_table[slot].initialized && lock_table[slot].app == app)
- lock_table[slot].app = NULL;
-
- /* Deallocate. */
- if (app->fnc.deinit)
- {
- app->fnc.deinit (app);
- app->fnc.deinit = NULL;
- }
+ {
+ if (lock_table[slot].last_app)
+ deallocate_app (lock_table[slot].last_app);
+ lock_table[slot].last_app = lock_table[slot].app;
+ lock_table[slot].app = NULL;
+ return;
+ }
- xfree (app->serialno);
- xfree (app);
+ log_debug ("application missing in lock table - deallocating anyway\n");
+ deallocate_app (app);
}
diff --git a/scd/command.c b/scd/command.c
index d556822a2..1b7a8f67e 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -47,7 +47,7 @@
#define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t))
-/* Macro to flag a a removed card. */
+/* Macro to flag a removed card. */
#define TEST_CARD_REMOVAL(c,r) \
do { \
int _r = (r); \
@@ -108,6 +108,8 @@ update_card_removed (int slot, int value)
if (sl->ctrl_backlink
&& sl->ctrl_backlink->reader_slot == slot)
sl->card_removed = value;
+ if (value)
+ application_notify_card_removed (slot);
}
@@ -142,7 +144,7 @@ do_reset (ctrl_t ctrl, int do_close)
struct server_local_s *sl;
/* If we are the only session with the reader open we may close
- it. If not, do a reset unless the a lock is held on the
+ it. If not, do a reset unless a lock is held on the
reader. */
for (sl=session_list; sl; sl = sl->next_session)
if (sl != ctrl->server_local
@@ -257,7 +259,12 @@ open_card (ctrl_t ctrl, const char *apptype)
return gpg_error (GPG_ERR_LOCKED);
if (ctrl->app_ctx)
- return 0; /* Already initialized for one specific application. */
+ {
+ /* Already initialized for one specific application. Need to
+ check that the client didn't requested a specific application
+ different from the one in use. */
+ return check_application_conflict (ctrl, apptype);
+ }
if (ctrl->reader_slot != -1)
slot = ctrl->reader_slot;
@@ -1201,7 +1208,7 @@ cmd_checkpin (assuan_context_t ctx, char *line)
Grant exclusive card access to this session. Note that there is
no lock counter used and a second lock from the same session will
- get ignore. A single unlock (or RESET) unlocks the session.
+ be ignored. A single unlock (or RESET) unlocks the session.
Return GPG_ERR_LOCKED if another session has locked the reader.
If the option --wait is given the command will wait until a
@@ -1225,9 +1232,12 @@ cmd_lock (assuan_context_t ctx, char *line)
#ifdef USE_GNU_PTH
if (rc && has_option (line, "--wait"))
{
+ rc = 0;
pth_sleep (1); /* Better implement an event mechanism. However,
for card operations this should be
sufficient. */
+ /* FIXME: Need to check that the connection is still alive.
+ This can be done by issuing status messages. */
goto retry;
}
#endif /*USE_GNU_PTH*/
@@ -1293,6 +1303,36 @@ cmd_getinfo (assuan_context_t ctx, char *line)
}
+/* RESTART
+
+ Restart the current connection; this is a kind of warn reset. It
+ deletes the context used by this connection but does not send a
+ RESET to the card. Thus the card itself won't get reset.
+
+ This is used by gpg-agent to reuse a primary pipe connection and
+ may be used by clients to backup from a conflict in the serial
+ command; i.e. to select another application.
+*/
+
+static int
+cmd_restart (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+
+ if (ctrl->app_ctx)
+ {
+ release_application (ctrl->app_ctx);
+ ctrl->app_ctx = NULL;
+ }
+ if (locked_session && ctrl->server_local == locked_session)
+ {
+ locked_session = NULL;
+ log_info ("implicitly unlocking due to RESTART\n");
+ }
+ return 0;
+}
+
+
/* Tell the assuan library about our commands */
@@ -1323,6 +1363,7 @@ register_commands (assuan_context_t ctx)
{ "LOCK", cmd_lock },
{ "UNLOCK", cmd_unlock },
{ "GETINFO", cmd_getinfo },
+ { "RESTART", cmd_restart },
{ NULL }
};
int i, rc;