Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F22947943
cardglue.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
40 KB
Subscribers
None
cardglue.c
View Options
/* cardglue.c - mainly dispatcher for card related functions.
* Copyright (C) 2003, 2004, 2005, 2006, 2009 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG 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.
*
* GnuPG 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 <http://www.gnu.org/licenses/>.
*/
#include
<config.h>
#ifndef ENABLE_CARD_SUPPORT
#error not configured for card support.
#endif
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<errno.h>
#include
<stdarg.h>
#include
<assert.h>
#include
"options.h"
#include
"packet.h"
#include
"errors.h"
#include
"memory.h"
#include
"util.h"
#include
"main.h"
#include
"status.h"
#include
"ttyio.h"
#include
"i18n.h"
#include
"cardglue.h"
#include
"apdu.h"
#include
"app-common.h"
struct
ctrl_ctx_s
{
assuan_error_t
(
*
status_cb
)(
void
*
opaque
,
const
char
*
line
);
void
*
status_cb_arg
;
};
struct
pincb_parm_s
{
const
char
*
sn
;
};
struct
writekey_parm_s
{
assuan_context_t
ctx
;
const
unsigned
char
*
keydata
;
size_t
keydatalen
;
};
static
char
*
default_reader_port
;
static
app_t
current_app
;
/* Local prototypes. */
static
assuan_error_t
learn_status_cb
(
void
*
opaque
,
const
char
*
line
);
/* To avoid cluttering the code with bunches of ifdefs we use a few
dummy functions instead and defines. */
#ifndef ENABLE_AGENT_SUPPORT
#define ASSUAN_LINELENGTH 100
static
assuan_context_t
agent_open
(
int
try
,
const
char
*
orig_codeset
)
{
return
NULL
;
}
void
agent_close
(
assuan_context_t
ctx
)
{
}
const
char
*
assuan_strerror
(
assuan_error_t
err
)
{
return
"no Assuan support"
;
}
assuan_error_t
assuan_transact
(
assuan_context_t
ctx
,
const
char
*
command
,
assuan_error_t
(
*
data_cb
)(
void
*
,
const
void
*
,
size_t
),
void
*
data_cb_arg
,
assuan_error_t
(
*
inquire_cb
)(
void
*
,
const
char
*
),
void
*
inquire_cb_arg
,
assuan_error_t
(
*
status_cb
)(
void
*
,
const
char
*
),
void
*
status_cb_arg
)
{
return
100
;
/* ASSUAN_NOT_IMPLEMENTED */
}
assuan_error_t
assuan_send_data
(
assuan_context_t
ctx
,
const
void
*
buffer
,
size_t
length
)
{
return
100
;
/* ASSUAN_NOT_IMPLEMENTED */
}
#endif
/*!ENABLE_AGENT_SUPPORT*/
/* Create a serialno/fpr string from the serial number and the secret
key. caller must free the returned string. There is no error
return. [Taken from 1.9's keyid.c]*/
char
*
serialno_and_fpr_from_sk
(
const
unsigned
char
*
sn
,
size_t
snlen
,
PKT_secret_key
*
sk
)
{
unsigned
char
fpr
[
MAX_FINGERPRINT_LEN
];
size_t
fprlen
;
char
*
buffer
,
*
p
;
int
i
;
fingerprint_from_sk
(
sk
,
fpr
,
&
fprlen
);
buffer
=
p
=
xmalloc
(
snlen
*
2
+
1
+
fprlen
*
2
+
1
);
for
(
i
=
0
;
i
<
snlen
;
i
++
,
p
+=
2
)
sprintf
(
p
,
"%02X"
,
sn
[
i
]);
*
p
++
=
'/'
;
for
(
i
=
0
;
i
<
fprlen
;
i
++
,
p
+=
2
)
sprintf
(
p
,
"%02X"
,
fpr
[
i
]);
*
p
=
0
;
return
buffer
;
}
/* Send a line with status information via assuan and escape all given
buffers. The variable elements are pairs of (char *, size_t),
terminated with a (NULL, 0). */
void
send_status_info
(
ctrl_t
ctrl
,
const
char
*
keyword
,
...)
{
va_list
arg_ptr
;
const
unsigned
char
*
value
;
size_t
valuelen
;
char
buf
[
950
],
*
p
;
size_t
n
;
va_start
(
arg_ptr
,
keyword
);
p
=
buf
;
n
=
0
;
valuelen
=
strlen
(
keyword
);
for
(
;
valuelen
&&
n
<
DIM
(
buf
)
-2
;
n
++
,
valuelen
--
,
keyword
++
)
*
p
++
=
*
keyword
;
while
(
(
value
=
va_arg
(
arg_ptr
,
const
unsigned
char
*
))
)
{
valuelen
=
va_arg
(
arg_ptr
,
size_t
);
if
(
!
valuelen
)
continue
;
/* empty buffer */
if
(
n
)
{
*
p
++
=
' '
;
n
++
;
}
for
(
;
valuelen
&&
n
<
DIM
(
buf
)
-2
;
n
++
,
valuelen
--
,
value
++
)
{
if
(
*
value
<
' '
||
*
value
==
'+'
)
{
sprintf
(
p
,
"%%%02X"
,
*
value
);
p
+=
3
;
}
else
if
(
*
value
==
' '
)
*
p
++
=
'+'
;
else
*
p
++
=
*
value
;
}
}
*
p
=
0
;
if
(
ctrl
&&
ctrl
->
status_cb
)
ctrl
->
status_cb
(
ctrl
->
status_cb_arg
,
buf
);
va_end
(
arg_ptr
);
}
/* Send a ready formatted status line via assuan. */
void
send_status_direct
(
ctrl_t
ctrl
,
const
char
*
keyword
,
const
char
*
args
)
{
char
buf
[
950
];
if
(
strchr
(
args
,
'\n'
))
log_error
(
"error: LF detected in status line - not sending
\n
"
);
else
{
snprintf
(
buf
,
sizeof
buf
,
"%s%s%s"
,
keyword
,
args
?
" "
:
""
,
args
?
args
:
""
);
if
(
ctrl
&&
ctrl
->
status_cb
)
ctrl
->
status_cb
(
ctrl
->
status_cb_arg
,
buf
);
}
}
void
gcry_mpi_release
(
MPI
a
)
{
mpi_free
(
a
);
}
MPI
gcry_mpi_set_opaque
(
MPI
a
,
void
*
p
,
unsigned
int
len
)
{
return
mpi_set_opaque
(
a
,
p
,
len
);
}
/* Replacement function of the Libgcrypt onewhich is used in gnupg
1.9. Thus function computes the digest of ALGO from the data in
BUFFER of LENGTH. ALGO must be supported. */
void
gcry_md_hash_buffer
(
int
algo
,
void
*
digest
,
const
void
*
buffer
,
size_t
length
)
{
MD_HANDLE
h
=
md_open
(
algo
,
0
);
if
(
!
h
)
BUG
();
md_write
(
h
,
(
byte
*
)
buffer
,
length
);
md_final
(
h
);
memcpy
(
digest
,
md_read
(
h
,
algo
),
md_digest_length
(
algo
));
md_close
(
h
);
}
/* This function simply returns the name of the algorithm or some
constant string when there is no algo. It will never return
NULL. */
const
char
*
gcry_md_algo_name
(
int
algorithm
)
{
const
char
*
s
=
digest_algo_to_string
(
algorithm
);
return
s
?
s
:
"?"
;
}
/* This is a limited version of the one in 1.9 but it should be
sufficient here. */
void
log_printf
(
const
char
*
fmt
,
...)
{
va_list
arg_ptr
;
va_start
(
arg_ptr
,
fmt
);
vfprintf
(
log_stream
(),
fmt
,
arg_ptr
);
va_end
(
arg_ptr
);
}
/* Print a hexdump of BUFFER. With TEXT of NULL print just the raw
dump, with TEXT just an empty string, print a trailing linefeed,
otherwise print an entire debug line. */
void
log_printhex
(
const
char
*
text
,
const
void
*
buffer
,
size_t
length
)
{
if
(
text
&&
*
text
)
log_debug
(
"%s "
,
text
);
if
(
length
)
{
const
unsigned
char
*
p
=
buffer
;
log_printf
(
"%02X"
,
*
p
);
for
(
length
--
,
p
++
;
length
--
;
p
++
)
log_printf
(
" %02X"
,
*
p
);
}
if
(
text
)
log_printf
(
"
\n
"
);
}
void
app_set_default_reader_port
(
const
char
*
portstr
)
{
xfree
(
default_reader_port
);
default_reader_port
=
portstr
?
xstrdup
(
portstr
)
:
NULL
;
}
void
card_set_reader_port
(
const
char
*
portstr
)
{
app_set_default_reader_port
(
portstr
);
}
/* Retrieve the serial number and the time of the last update of the
card. The serial number is returned as a malloced string (hex
encoded) in SERIAL and the time of update is returned in STAMP. If
no update time is available the returned value is 0. Caller must
free SERIAL unless the function returns an error. */
int
app_get_serial_and_stamp
(
app_t
app
,
char
**
serial
,
time_t
*
stamp
)
{
unsigned
char
*
buf
,
*
p
;
int
i
;
if
(
!
app
||
!
serial
||
!
stamp
)
return
gpg_error
(
GPG_ERR_INV_VALUE
);
*
serial
=
NULL
;
*
stamp
=
0
;
/* not available */
buf
=
xtrymalloc
(
app
->
serialnolen
*
2
+
1
);
if
(
!
buf
)
return
gpg_error_from_errno
(
errno
);
for
(
p
=
buf
,
i
=
0
;
i
<
app
->
serialnolen
;
p
+=
2
,
i
++
)
sprintf
(
p
,
"%02X"
,
app
->
serialno
[
i
]);
*
p
=
0
;
*
serial
=
buf
;
return
0
;
}
/* Release the card info structure. */
void
agent_release_card_info
(
struct
agent_card_info_s
*
info
)
{
int
i
;
if
(
!
info
)
return
;
xfree
(
info
->
serialno
);
info
->
serialno
=
NULL
;
xfree
(
info
->
apptype
);
info
->
apptype
=
NULL
;
xfree
(
info
->
disp_name
);
info
->
disp_name
=
NULL
;
xfree
(
info
->
disp_lang
);
info
->
disp_lang
=
NULL
;
xfree
(
info
->
pubkey_url
);
info
->
pubkey_url
=
NULL
;
xfree
(
info
->
login_data
);
info
->
login_data
=
NULL
;
info
->
fpr1valid
=
info
->
fpr2valid
=
info
->
fpr3valid
=
0
;
info
->
cafpr1valid
=
info
->
cafpr2valid
=
info
->
cafpr3valid
=
0
;
for
(
i
=
0
;
i
<
4
;
i
++
)
{
xfree
(
info
->
private_do
[
i
]);
info
->
private_do
[
i
]
=
NULL
;
}
}
/* Print an error message for a failed assuan_transact and return a
gpg error code. No error is printed if RC is 0. */
static
gpg_error_t
test_transact
(
int
rc
,
const
char
*
command
)
{
if
(
!
rc
)
return
0
;
log_error
(
"sending command `%s' to agent failed: %s
\n
"
,
command
,
assuan_strerror
(
rc
));
return
gpg_error
(
GPG_ERR_CARD
);
}
/* Try to open a card using an already running agent. Prepare a
proper application context and return it. */
static
app_t
open_card_via_agent
(
int
*
scd_available
)
{
assuan_context_t
ctx
;
app_t
app
;
struct
agent_card_info_s
info
;
int
rc
;
*
scd_available
=
0
;
ctx
=
agent_open
(
1
,
NULL
);
if
(
!
ctx
)
return
NULL
;
/* Request the serialnumber of the card. If we get
NOT_SUPPORTED or NO_SCDAEMON back, the gpg-agent either has
disabled scdaemon or it can't be used. We close the connection
in this case and use our own code. This may happen if just the
gpg-agent has been installed for the sake of passphrase
caching. */
memset
(
&
info
,
0
,
sizeof
info
);
rc
=
assuan_transact
(
ctx
,
"SCD SERIALNO openpgp"
,
NULL
,
NULL
,
NULL
,
NULL
,
learn_status_cb
,
&
info
);
if
(
rc
)
{
if
((
rc
&
0xffff
)
==
60
||
(
rc
&
0xffff
)
==
119
)
;
/* No scdaemon available to gpg-agent. */
else
{
write_status_text
(
STATUS_CARDCTRL
,
"4"
);
log_info
(
"selecting openpgp failed: %s
\n
"
,
assuan_strerror
(
rc
));
*
scd_available
=
1
;
}
agent_release_card_info
(
&
info
);
agent_close
(
ctx
);
return
NULL
;
}
app
=
xcalloc
(
1
,
sizeof
*
app
);
app
->
assuan_ctx
=
ctx
;
return
app
;
}
/* Open the current card and select the openpgp application. Return
an APP context handle to be used for further procesing or NULL on
error or if no OpenPGP application exists.*/
static
app_t
open_card
(
void
)
{
int
slot
=
-1
;
int
rc
;
app_t
app
;
int
did_shutdown
=
0
;
int
retry_count
=
0
;
/* First check whether we can contact a gpg-agent and divert all
operation to it. This is required because gpg as well as the
agent require exclusive access to the reader. */
if
(
opt
.
use_agent
)
{
int
scd_available
;
app
=
open_card_via_agent
(
&
scd_available
);
if
(
app
)
goto
ready
;
/* Yes, there is a agent with a usable card, go that way. */
if
(
scd_available
)
return
NULL
;
/* Agent available but card problem. */
}
/* No agent or usable agent, thus we do it on our own. */
card_close
();
retry
:
if
(
did_shutdown
)
apdu_reset
(
slot
);
else
{
slot
=
apdu_open_reader
(
default_reader_port
);
if
(
slot
==
-1
)
{
write_status_text
(
STATUS_CARDCTRL
,
"5"
);
log_error
(
_
(
"card reader not available
\n
"
));
return
NULL
;
}
}
app
=
xcalloc
(
1
,
sizeof
*
app
);
app
->
slot
=
slot
;
rc
=
app_select_openpgp
(
app
);
if
(
opt
.
limit_card_insert_tries
&&
++
retry_count
>=
opt
.
limit_card_insert_tries
)
;
else
if
(
rc
&&
!
opt
.
batch
)
{
write_status_text
(
STATUS_CARDCTRL
,
"1"
);
did_shutdown
=
!!
apdu_shutdown_reader
(
slot
);
if
(
cpr_get_answer_okay_cancel
(
"cardctrl.insert_card.okay"
,
_
(
"Please insert the card and hit return or enter 'c' to cancel: "
),
1
)
)
{
if
(
!
did_shutdown
)
apdu_close_reader
(
slot
);
xfree
(
app
);
goto
retry
;
}
}
if
(
rc
)
{
write_status_text
(
STATUS_CARDCTRL
,
"4"
);
log_info
(
_
(
"selecting openpgp failed: %s
\n
"
),
gpg_strerror
(
rc
));
apdu_close_reader
(
slot
);
xfree
(
app
);
return
NULL
;
}
ready
:
app
->
ref_count
=
1
;
current_app
=
app
;
if
(
is_status_enabled
()
)
{
int
i
;
char
*
p
,
*
buf
;
buf
=
xmalloc
(
5
+
app
->
serialnolen
*
2
+
1
);
p
=
stpcpy
(
buf
,
"3 "
);
for
(
i
=
0
;
i
<
app
->
serialnolen
;
p
+=
2
,
i
++
)
sprintf
(
p
,
"%02X"
,
app
->
serialno
[
i
]);
write_status_text
(
STATUS_CARDCTRL
,
buf
);
xfree
(
buf
);
}
return
app
;
}
void
card_close
(
void
)
{
if
(
current_app
)
{
app_t
app
=
current_app
;
current_app
=
NULL
;
if
(
app
->
assuan_ctx
)
agent_close
(
app
->
assuan_ctx
);
else
apdu_close_reader
(
app
->
slot
);
xfree
(
app
);
}
}
/* Format a cache ID from the serialnumber in SN and return it as an
allocated string. In case of an error NULL is returned. */
static
char
*
format_cacheid
(
const
char
*
sn
)
{
const
char
*
s
;
size_t
snlen
;
char
*
cacheid
=
NULL
;
/* The serialnumber we use for a card is "CARDSN:serialno". Where
serialno is the BCD string (i.e. hex string) with the full
number. The serial number expect here constsis of hexdigits
followed by other characters, we cut off these other
characters. */
if
(
sn
)
{
for
(
s
=
sn
,
snlen
=
0
;
hexdigitp
(
s
);
s
++
,
snlen
++
)
;
if
(
snlen
==
32
)
{
/* Yes, this looks indeed like an OpenPGP card S/N. */
cacheid
=
xtrymalloc
(
7
+
snlen
+
1
);
if
(
cacheid
)
{
memcpy
(
cacheid
,
"CARDSN:"
,
7
);
memcpy
(
cacheid
+
7
,
sn
,
snlen
);
cacheid
[
7
+
snlen
]
=
0
;
}
}
}
return
cacheid
;
}
/* If RC is not 0, write an appropriate status message. */
static
void
status_sc_op_failure
(
int
rc
)
{
if
(
rc
==
G10ERR_CANCELED
)
write_status_text
(
STATUS_SC_OP_FAILURE
,
"1"
);
else
if
(
rc
==
G10ERR_BAD_PASS
)
write_status_text
(
STATUS_SC_OP_FAILURE
,
"2"
);
else
if
(
rc
)
write_status
(
STATUS_SC_OP_FAILURE
);
}
/* Check that the serial number of the current card (as described by
APP) matches SERIALNO. If there is no match and we are not in
batch mode, present a prompt to insert the desired card. The
function returnd 0 if the present card is okay, -1 if the user
selected to insert a new card or an error value. Note that the
card context will be closed in all cases except for 0 as return
value and if it was possible to merely shutdown the reader. */
static
int
check_card_serialno
(
app_t
app
,
const
char
*
serialno
)
{
const
char
*
s
;
int
ask
=
0
;
int
n
;
for
(
s
=
serialno
,
n
=
0
;
*
s
!=
'/'
&&
hexdigitp
(
s
);
s
++
,
n
++
)
;
if
(
n
!=
32
)
{
log_error
(
"invalid serial number in keyring detected
\n
"
);
return
gpg_error
(
GPG_ERR_INV_ID
);
}
if
(
app
->
serialnolen
!=
16
)
ask
=
1
;
for
(
s
=
serialno
,
n
=
0
;
!
ask
&&
n
<
16
;
s
+=
2
,
n
++
)
if
(
app
->
serialno
[
n
]
!=
xtoi_2
(
s
))
ask
=
1
;
if
(
ask
)
{
char
buf
[
5
+
32
+
1
];
int
did_shutdown
=
0
;
if
(
current_app
&&
!
apdu_shutdown_reader
(
current_app
->
slot
))
did_shutdown
=
1
;
else
card_close
();
if
(
!
opt
.
batch
)
tty_printf
(
_
(
"Please remove the current card and "
"insert the one with serial number:
\n
"
" %.*s
\n
"
),
32
,
serialno
);
sprintf
(
buf
,
"1 %.32s"
,
serialno
);
write_status_text
(
STATUS_CARDCTRL
,
buf
);
if
(
!
opt
.
batch
&&
cpr_get_answer_okay_cancel
(
"cardctrl.change_card.okay"
,
_
(
"Hit return when ready "
"or enter 'c' to cancel: "
),
1
)
)
{
card_close
();
return
-1
;
}
if
(
did_shutdown
)
apdu_reset
(
current_app
->
slot
);
else
card_close
();
return
gpg_error
(
GPG_ERR_INV_ID
);
}
return
0
;
}
/* Take a 20 byte hexencoded string and put it into the the provided
20 byte buffer FPR in binary format. */
static
int
unhexify_fpr
(
const
char
*
hexstr
,
unsigned
char
*
fpr
)
{
const
char
*
s
;
int
n
;
for
(
s
=
hexstr
,
n
=
0
;
hexdigitp
(
s
);
s
++
,
n
++
)
;
if
(
*
s
||
(
n
!=
40
))
return
0
;
/* no fingerprint (invalid or wrong length). */
n
/=
2
;
for
(
s
=
hexstr
,
n
=
0
;
*
s
;
s
+=
2
,
n
++
)
fpr
[
n
]
=
xtoi_2
(
s
);
return
1
;
/* okay */
}
/* Take the serial number from LINE and return it verbatim in a newly
allocated string. We make sure that only hex characters are
returned. */
static
char
*
store_serialno
(
const
char
*
line
)
{
const
char
*
s
;
char
*
p
;
for
(
s
=
line
;
hexdigitp
(
s
);
s
++
)
;
p
=
xmalloc
(
s
+
1
-
line
);
memcpy
(
p
,
line
,
s
-
line
);
p
[
s
-
line
]
=
0
;
return
p
;
}
/* Return a new malloced string by unescaping the string S. Escaping
is percent escaping and '+'/space mapping. A binary nul will
silently be replaced by a 0xFF. Function returns NULL to indicate
an out of memory status. */
static
char
*
unescape_status_string
(
const
unsigned
char
*
s
)
{
return
unescape_percent_string
(
s
);
}
static
assuan_error_t
learn_status_cb
(
void
*
opaque
,
const
char
*
line
)
{
struct
agent_card_info_s
*
parm
=
opaque
;
const
char
*
keyword
=
line
;
int
keywordlen
;
int
i
;
/* log_debug ("got status line `%s'\n", line); */
for
(
keywordlen
=
0
;
*
line
&&
!
spacep
(
line
);
line
++
,
keywordlen
++
)
;
while
(
spacep
(
line
))
line
++
;
if
(
keywordlen
==
8
&&
!
memcmp
(
keyword
,
"SERIALNO"
,
keywordlen
))
{
xfree
(
parm
->
serialno
);
parm
->
serialno
=
store_serialno
(
line
);
parm
->
is_v2
=
(
strlen
(
parm
->
serialno
)
>=
16
&&
xtoi_2
(
parm
->
serialno
+
12
)
>=
2
);
}
else
if
(
keywordlen
==
7
&&
!
memcmp
(
keyword
,
"APPTYPE"
,
keywordlen
))
{
xfree
(
parm
->
apptype
);
parm
->
apptype
=
unescape_status_string
(
line
);
}
else
if
(
keywordlen
==
9
&&
!
memcmp
(
keyword
,
"DISP-NAME"
,
keywordlen
))
{
xfree
(
parm
->
disp_name
);
parm
->
disp_name
=
unescape_percent_string
(
line
);
}
else
if
(
keywordlen
==
9
&&
!
memcmp
(
keyword
,
"DISP-LANG"
,
keywordlen
))
{
xfree
(
parm
->
disp_lang
);
parm
->
disp_lang
=
unescape_percent_string
(
line
);
}
else
if
(
keywordlen
==
8
&&
!
memcmp
(
keyword
,
"DISP-SEX"
,
keywordlen
))
{
parm
->
disp_sex
=
*
line
==
'1'
?
1
:
*
line
==
'2'
?
2
:
0
;
}
else
if
(
keywordlen
==
10
&&
!
memcmp
(
keyword
,
"PUBKEY-URL"
,
keywordlen
))
{
xfree
(
parm
->
pubkey_url
);
parm
->
pubkey_url
=
unescape_percent_string
(
line
);
}
else
if
(
keywordlen
==
10
&&
!
memcmp
(
keyword
,
"LOGIN-DATA"
,
keywordlen
))
{
xfree
(
parm
->
login_data
);
parm
->
login_data
=
unescape_percent_string
(
line
);
}
else
if
(
keywordlen
==
11
&&
!
memcmp
(
keyword
,
"SIG-COUNTER"
,
keywordlen
))
{
parm
->
sig_counter
=
strtoul
(
line
,
NULL
,
0
);
}
else
if
(
keywordlen
==
10
&&
!
memcmp
(
keyword
,
"CHV-STATUS"
,
keywordlen
))
{
char
*
p
,
*
buf
;
buf
=
p
=
unescape_percent_string
(
line
);
if
(
buf
)
{
while
(
spacep
(
p
))
p
++
;
parm
->
chv1_cached
=
atoi
(
p
);
while
(
*
p
&&
!
spacep
(
p
))
p
++
;
while
(
spacep
(
p
))
p
++
;
for
(
i
=
0
;
*
p
&&
i
<
3
;
i
++
)
{
parm
->
chvmaxlen
[
i
]
=
atoi
(
p
);
while
(
*
p
&&
!
spacep
(
p
))
p
++
;
while
(
spacep
(
p
))
p
++
;
}
for
(
i
=
0
;
*
p
&&
i
<
3
;
i
++
)
{
parm
->
chvretry
[
i
]
=
atoi
(
p
);
while
(
*
p
&&
!
spacep
(
p
))
p
++
;
while
(
spacep
(
p
))
p
++
;
}
xfree
(
buf
);
}
}
else
if
(
keywordlen
==
6
&&
!
memcmp
(
keyword
,
"EXTCAP"
,
keywordlen
))
{
char
*
p
,
*
p2
,
*
buf
;
int
abool
;
buf
=
p
=
unescape_status_string
(
line
);
if
(
buf
)
{
for
(
p
=
strtok
(
buf
,
" "
);
p
;
p
=
strtok
(
NULL
,
" "
))
{
p2
=
strchr
(
p
,
'='
);
if
(
p2
)
{
*
p2
++
=
0
;
abool
=
(
*
p2
==
'1'
);
if
(
!
strcmp
(
p
,
"ki"
))
parm
->
extcap
.
ki
=
abool
;
else
if
(
!
strcmp
(
p
,
"aac"
))
parm
->
extcap
.
aac
=
abool
;
}
}
xfree
(
buf
);
}
}
else
if
(
keywordlen
==
7
&&
!
memcmp
(
keyword
,
"KEY-FPR"
,
keywordlen
))
{
int
no
=
atoi
(
line
);
while
(
*
line
&&
!
spacep
(
line
))
line
++
;
while
(
spacep
(
line
))
line
++
;
if
(
no
==
1
)
parm
->
fpr1valid
=
unhexify_fpr
(
line
,
parm
->
fpr1
);
else
if
(
no
==
2
)
parm
->
fpr2valid
=
unhexify_fpr
(
line
,
parm
->
fpr2
);
else
if
(
no
==
3
)
parm
->
fpr3valid
=
unhexify_fpr
(
line
,
parm
->
fpr3
);
}
else
if
(
keywordlen
==
8
&&
!
memcmp
(
keyword
,
"KEY-TIME"
,
keywordlen
))
{
int
no
=
atoi
(
line
);
while
(
*
line
&&
!
spacep
(
line
))
line
++
;
while
(
spacep
(
line
))
line
++
;
if
(
no
==
1
)
parm
->
fpr1time
=
strtoul
(
line
,
NULL
,
10
);
else
if
(
no
==
2
)
parm
->
fpr2time
=
strtoul
(
line
,
NULL
,
10
);
else
if
(
no
==
3
)
parm
->
fpr3time
=
strtoul
(
line
,
NULL
,
10
);
}
else
if
(
keywordlen
==
6
&&
!
memcmp
(
keyword
,
"CA-FPR"
,
keywordlen
))
{
int
no
=
atoi
(
line
);
while
(
*
line
&&
!
spacep
(
line
))
line
++
;
while
(
spacep
(
line
))
line
++
;
if
(
no
==
1
)
parm
->
cafpr1valid
=
unhexify_fpr
(
line
,
parm
->
cafpr1
);
else
if
(
no
==
2
)
parm
->
cafpr2valid
=
unhexify_fpr
(
line
,
parm
->
cafpr2
);
else
if
(
no
==
3
)
parm
->
cafpr3valid
=
unhexify_fpr
(
line
,
parm
->
cafpr3
);
}
else
if
(
keywordlen
==
12
&&
!
memcmp
(
keyword
,
"PRIVATE-DO-"
,
11
)
&&
strchr
(
"1234"
,
keyword
[
11
]))
{
int
no
=
keyword
[
11
]
-
'1'
;
assert
(
no
>=
0
&&
no
<=
3
);
xfree
(
parm
->
private_do
[
no
]);
parm
->
private_do
[
no
]
=
unescape_percent_string
(
line
);
}
else
if
(
keywordlen
==
8
&&
!
memcmp
(
keyword
,
"KEY-ATTR"
,
keywordlen
))
{
int
keyno
,
algo
,
nbits
;
sscanf
(
line
,
"%d %d %d"
,
&
keyno
,
&
algo
,
&
nbits
);
keyno
--
;
if
(
keyno
>=
0
&&
keyno
<
DIM
(
parm
->
key_attr
))
{
parm
->
key_attr
[
keyno
].
algo
=
algo
;
parm
->
key_attr
[
keyno
].
nbits
=
nbits
;
}
}
return
0
;
}
/* Return card info. */
int
agent_learn
(
struct
agent_card_info_s
*
info
)
{
app_t
app
;
int
rc
;
struct
ctrl_ctx_s
ctrl
;
time_t
stamp
;
char
*
serial
;
app
=
current_app
?
current_app
:
open_card
();
if
(
!
app
)
return
gpg_error
(
GPG_ERR_CARD
);
memset
(
info
,
0
,
sizeof
*
info
);
if
(
app
->
assuan_ctx
)
{
rc
=
assuan_transact
(
app
->
assuan_ctx
,
"SCD LEARN --force"
,
NULL
,
NULL
,
NULL
,
NULL
,
learn_status_cb
,
info
);
rc
=
test_transact
(
rc
,
"SCD LEARN"
);
}
else
{
memset
(
&
ctrl
,
0
,
sizeof
ctrl
);
ctrl
.
status_cb
=
learn_status_cb
;
ctrl
.
status_cb_arg
=
info
;
rc
=
app_get_serial_and_stamp
(
app
,
&
serial
,
&
stamp
);
if
(
!
rc
)
{
send_status_info
(
&
ctrl
,
"SERIALNO"
,
serial
,
strlen
(
serial
),
NULL
,
0
);
xfree
(
serial
);
rc
=
app
->
fnc
.
learn_status
(
app
,
&
ctrl
,
0
);
}
}
if
(
!
rc
)
agent_scd_getattr
(
"KEY-ATTR"
,
info
);
return
rc
;
}
/* Get an attribute from the card. Make sure info is initialized. */
int
agent_scd_getattr
(
const
char
*
name
,
struct
agent_card_info_s
*
info
)
{
int
rc
;
app_t
app
;
struct
ctrl_ctx_s
ctrl
;
app
=
current_app
?
current_app
:
open_card
();
if
(
!
app
)
return
gpg_error
(
GPG_ERR_CARD
);
if
(
app
->
assuan_ctx
)
{
char
line
[
ASSUAN_LINELENGTH
];
/* We assume that NAME does not need escaping. */
if
(
12
+
strlen
(
name
)
>
DIM
(
line
)
-1
)
return
gpg_error
(
GPG_ERR_CARD
);
stpcpy
(
stpcpy
(
line
,
"SCD GETATTR "
),
name
);
rc
=
test_transact
(
assuan_transact
(
app
->
assuan_ctx
,
line
,
NULL
,
NULL
,
NULL
,
NULL
,
learn_status_cb
,
info
),
"SCD GETATTR"
);
}
else
{
ctrl
.
status_cb
=
learn_status_cb
;
ctrl
.
status_cb_arg
=
info
;
rc
=
app
->
fnc
.
getattr
(
app
,
&
ctrl
,
name
);
}
return
rc
;
}
static
int
pin_cb
(
void
*
opaque
,
const
char
*
info
,
char
**
retstr
)
{
struct
pincb_parm_s
*
parm
=
opaque
;
char
*
value
;
int
canceled
;
int
isadmin
=
0
;
int
newpin
=
0
;
const
char
*
again_text
=
NULL
;
const
char
*
ends
,
*
s
;
char
*
cacheid
=
NULL
;
*
retstr
=
NULL
;
/* log_debug ("asking for PIN '%s'\n", info); */
/* We use a special prefix to check whether the Admin PIN has been
requested. */
if
(
info
&&
*
info
==
'|'
&&
(
ends
=
strchr
(
info
+
1
,
'|'
)))
{
for
(
s
=
info
+
1
;
s
<
ends
;
s
++
)
{
if
(
*
s
==
'A'
)
isadmin
=
1
;
else
if
(
*
s
==
'N'
)
newpin
=
1
;
}
info
=
ends
+
1
;
}
else
if
(
info
&&
*
info
==
'|'
)
log_debug
(
"pin_cb called without proper PIN info hack
\n
"
);
/* If we are not requesting a new PIN and we are not requesting an
AdminPIN, compute a string to be used as the cacheID for
gpg-agent. */
if
(
!
newpin
&&
!
isadmin
&&
parm
)
{
cacheid
=
format_cacheid
(
parm
->
sn
);
}
else
if
(
newpin
&&
parm
)
{
/* Make really sure that it is not cached anymore. */
agent_clear_pin_cache
(
parm
->
sn
);
}
again
:
if
(
is_status_enabled
())
{
if
(
parm
&&
parm
->
sn
&&
*
parm
->
sn
)
{
char
*
buf
=
xmalloc
(
10
+
strlen
(
parm
->
sn
)
+
1
);
strcpy
(
stpcpy
(
buf
,
isadmin
?
"OPENPGP 3 "
:
"OPENPGP 1 "
),
parm
->
sn
);
write_status_text
(
STATUS_NEED_PASSPHRASE_PIN
,
buf
);
xfree
(
buf
);
}
else
write_status_text
(
STATUS_NEED_PASSPHRASE_PIN
,
isadmin
?
"OPENPGP 3"
:
"OPENPGP 1"
);
}
value
=
ask_passphrase
(
info
,
again_text
,
newpin
&&
isadmin
?
"passphrase.adminpin.new.ask"
:
newpin
?
"passphrase.pin.new.ask"
:
isadmin
?
"passphrase.adminpin.ask"
:
"passphrase.pin.ask"
,
newpin
&&
isadmin
?
_
(
"Enter New Admin PIN: "
)
:
newpin
?
_
(
"Enter New PIN: "
)
:
isadmin
?
_
(
"Enter Admin PIN: "
)
:
_
(
"Enter PIN: "
),
cacheid
,
&
canceled
);
xfree
(
cacheid
);
cacheid
=
NULL
;
again_text
=
NULL
;
if
(
!
value
&&
canceled
)
return
G10ERR_CANCELED
;
else
if
(
!
value
)
return
G10ERR_GENERAL
;
if
(
newpin
)
{
char
*
value2
;
value2
=
ask_passphrase
(
info
,
NULL
,
"passphrase.pin.repeat"
,
_
(
"Repeat this PIN: "
),
NULL
,
&
canceled
);
if
(
!
value2
&&
canceled
)
{
xfree
(
value
);
return
G10ERR_CANCELED
;
}
else
if
(
!
value2
)
{
xfree
(
value
);
return
G10ERR_GENERAL
;
}
if
(
strcmp
(
value
,
value2
))
{
again_text
=
N_
(
"PIN not correctly repeated; try again"
);
xfree
(
value2
);
xfree
(
value
);
value
=
NULL
;
goto
again
;
}
xfree
(
value2
);
}
*
retstr
=
value
;
return
0
;
}
/* Send a SETATTR command to the SCdaemon. */
int
agent_scd_setattr
(
const
char
*
name
,
const
unsigned
char
*
value
,
size_t
valuelen
,
const
char
*
serialno
)
{
app_t
app
;
int
rc
;
struct
pincb_parm_s
parm
;
memset
(
&
parm
,
0
,
sizeof
parm
);
parm
.
sn
=
serialno
;
app
=
current_app
?
current_app
:
open_card
();
if
(
!
app
)
return
gpg_error
(
GPG_ERR_CARD
);
if
(
app
->
assuan_ctx
)
{
char
line
[
ASSUAN_LINELENGTH
];
char
*
p
;
/* We assume that NAME does not need escaping. */
if
(
12
+
strlen
(
name
)
>
DIM
(
line
)
-1
)
return
gpg_error
(
GPG_ERR_CARD
);
p
=
stpcpy
(
stpcpy
(
line
,
"SCD SETATTR "
),
name
);
*
p
++
=
' '
;
for
(;
valuelen
;
value
++
,
valuelen
--
)
{
if
(
p
>=
line
+
DIM
(
line
)
-5
)
return
gpg_error
(
GPG_ERR_CARD
);
if
(
*
value
<
' '
||
*
value
==
'+'
||
*
value
==
'%'
)
{
sprintf
(
p
,
"%%%02X"
,
*
value
);
p
+=
3
;
}
else
if
(
*
value
==
' '
)
*
p
++
=
'+'
;
else
*
p
++
=
*
value
;
}
*
p
=
0
;
rc
=
test_transact
(
assuan_transact
(
app
->
assuan_ctx
,
line
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
),
"SCD SETATTR"
);
}
else
{
rc
=
app
->
fnc
.
setattr
(
app
,
name
,
pin_cb
,
&
parm
,
value
,
valuelen
);
}
status_sc_op_failure
(
rc
);
return
rc
;
}
/* Handle a KEYDATA inquiry. Note, we only send the data,
assuan_transact takes care of flushing and writing the end */
static
assuan_error_t
inq_writekey_parms
(
void
*
opaque
,
const
char
*
keyword
)
{
struct
writekey_parm_s
*
parm
=
opaque
;
return
assuan_send_data
(
parm
->
ctx
,
parm
->
keydata
,
parm
->
keydatalen
);
}
/* Send a WRITEKEY command to the SCdaemon. */
int
agent_scd_writekey
(
int
keyno
,
const
char
*
serialno
,
const
unsigned
char
*
keydata
,
size_t
keydatalen
)
{
app_t
app
;
int
rc
;
char
line
[
ASSUAN_LINELENGTH
];
struct
pincb_parm_s
parm
;
memset
(
&
parm
,
0
,
sizeof
parm
);
parm
.
sn
=
serialno
;
app
=
current_app
?
current_app
:
open_card
();
if
(
!
app
)
return
gpg_error
(
GPG_ERR_CARD
);
if
(
app
->
assuan_ctx
)
{
struct
writekey_parm_s
parms
;
snprintf
(
line
,
DIM
(
line
)
-1
,
"SCD WRITEKEY --force OPENPGP.%d"
,
keyno
);
line
[
DIM
(
line
)
-1
]
=
0
;
parms
.
ctx
=
app
->
assuan_ctx
;
parms
.
keydata
=
keydata
;
parms
.
keydatalen
=
keydatalen
;
rc
=
test_transact
(
assuan_transact
(
app
->
assuan_ctx
,
line
,
NULL
,
NULL
,
inq_writekey_parms
,
&
parms
,
NULL
,
NULL
),
"SCD WRITEKEY"
);
}
else
{
snprintf
(
line
,
DIM
(
line
)
-1
,
"OPENPGP.%d"
,
keyno
);
line
[
DIM
(
line
)
-1
]
=
0
;
rc
=
app
->
fnc
.
writekey
(
app
,
NULL
,
line
,
0x0001
,
pin_cb
,
&
parm
,
keydata
,
keydatalen
);
}
status_sc_op_failure
(
rc
);
return
rc
;
}
static
assuan_error_t
genkey_status_cb
(
void
*
opaque
,
const
char
*
line
)
{
struct
agent_card_genkey_s
*
parm
=
opaque
;
const
char
*
keyword
=
line
;
int
keywordlen
;
/* log_debug ("got status line `%s'\n", line); */
for
(
keywordlen
=
0
;
*
line
&&
!
spacep
(
line
);
line
++
,
keywordlen
++
)
;
while
(
spacep
(
line
))
line
++
;
if
(
keywordlen
==
7
&&
!
memcmp
(
keyword
,
"KEY-FPR"
,
keywordlen
))
{
parm
->
fprvalid
=
unhexify_fpr
(
line
,
parm
->
fpr
);
}
if
(
keywordlen
==
8
&&
!
memcmp
(
keyword
,
"KEY-DATA"
,
keywordlen
))
{
MPI
a
;
const
char
*
name
=
line
;
char
*
buf
;
while
(
*
line
&&
!
spacep
(
line
))
line
++
;
while
(
spacep
(
line
))
line
++
;
buf
=
xmalloc
(
2
+
strlen
(
line
)
+
1
);
strcpy
(
stpcpy
(
buf
,
"0x"
),
line
);
a
=
mpi_alloc
(
300
);
if
(
mpi_fromstr
(
a
,
buf
)
)
log_error
(
"error parsing received key data
\n
"
);
else
if
(
*
name
==
'n'
&&
spacep
(
name
+
1
))
parm
->
n
=
a
;
else
if
(
*
name
==
'e'
&&
spacep
(
name
+
1
))
parm
->
e
=
a
;
else
{
log_info
(
"unknown parameter name in received key data
\n
"
);
mpi_free
(
a
);
}
xfree
(
buf
);
}
else
if
(
keywordlen
==
14
&&
!
memcmp
(
keyword
,
"KEY-CREATED-AT"
,
keywordlen
))
{
parm
->
created_at
=
(
u32
)
strtoul
(
line
,
NULL
,
10
);
}
return
0
;
}
/* Send a GENKEY command to the SCdaemon. */
int
agent_scd_genkey
(
struct
agent_card_genkey_s
*
info
,
int
keyno
,
int
force
,
const
char
*
serialno
,
u32
*
createtime
)
{
app_t
app
;
char
line
[
ASSUAN_LINELENGTH
];
struct
ctrl_ctx_s
ctrl
;
int
rc
;
struct
pincb_parm_s
parm
;
memset
(
&
parm
,
0
,
sizeof
parm
);
parm
.
sn
=
serialno
;
app
=
current_app
?
current_app
:
open_card
();
if
(
!
app
)
return
gpg_error
(
GPG_ERR_CARD
);
memset
(
info
,
0
,
sizeof
*
info
);
if
(
app
->
assuan_ctx
)
{
snprintf
(
line
,
DIM
(
line
)
-1
,
"SCD GENKEY %s%d"
,
force
?
"--force "
:
""
,
keyno
);
line
[
DIM
(
line
)
-1
]
=
0
;
rc
=
test_transact
(
assuan_transact
(
app
->
assuan_ctx
,
line
,
NULL
,
NULL
,
NULL
,
NULL
,
genkey_status_cb
,
info
),
"SCD GENKEY"
);
}
else
{
snprintf
(
line
,
DIM
(
line
)
-1
,
"%d"
,
keyno
);
ctrl
.
status_cb
=
genkey_status_cb
;
ctrl
.
status_cb_arg
=
info
;
rc
=
app
->
fnc
.
genkey
(
app
,
&
ctrl
,
line
,
force
?
1
:
0
,
*
createtime
,
pin_cb
,
&
parm
);
}
status_sc_op_failure
(
rc
);
return
rc
;
}
static
assuan_error_t
membuf_data_cb
(
void
*
opaque
,
const
void
*
buffer
,
size_t
length
)
{
membuf_t
*
data
=
opaque
;
if
(
buffer
)
put_membuf
(
data
,
buffer
,
length
);
return
0
;
}
/* Send a PKSIGN command to the SCdaemon. */
int
agent_scd_pksign
(
const
char
*
serialno
,
int
hashalgo
,
const
unsigned
char
*
indata
,
size_t
indatalen
,
unsigned
char
**
r_buf
,
size_t
*
r_buflen
)
{
struct
pincb_parm_s
parm
;
app_t
app
;
int
rc
;
*
r_buf
=
NULL
;
*
r_buflen
=
0
;
memset
(
&
parm
,
0
,
sizeof
parm
);
parm
.
sn
=
serialno
;
retry
:
app
=
current_app
?
current_app
:
open_card
();
if
(
!
app
)
return
gpg_error
(
GPG_ERR_CARD
);
if
(
app
->
assuan_ctx
)
{
char
*
p
,
line
[
ASSUAN_LINELENGTH
];
membuf_t
data
;
size_t
len
;
int
i
;
if
(
indatalen
*
2
+
50
>
DIM
(
line
))
return
gpg_error
(
GPG_ERR_GENERAL
);
p
=
stpcpy
(
line
,
"SCD SETDATA "
);
for
(
i
=
0
;
i
<
indatalen
;
i
++
,
p
+=
2
)
sprintf
(
p
,
"%02X"
,
indata
[
i
]);
rc
=
test_transact
(
assuan_transact
(
app
->
assuan_ctx
,
line
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
),
"SCD SETDATA"
);
if
(
!
rc
)
{
init_membuf
(
&
data
,
1024
);
snprintf
(
line
,
DIM
(
line
)
-1
,
"SCD PKSIGN %s%s"
,
hashalgo
==
GCRY_MD_SHA1
?
"--hash=sha1 "
:
hashalgo
==
GCRY_MD_SHA224
?
"--hash=sha224 "
:
hashalgo
==
GCRY_MD_SHA256
?
"--hash=sha256 "
:
hashalgo
==
GCRY_MD_SHA384
?
"--hash=sha384 "
:
hashalgo
==
GCRY_MD_SHA512
?
"--hash=sha512 "
:
hashalgo
==
GCRY_MD_RMD160
?
"--hash=rmd160 "
:
hashalgo
==
GCRY_MD_MD5
?
"--hash=md5 "
:
""
,
serialno
);
line
[
DIM
(
line
)
-1
]
=
0
;
rc
=
test_transact
(
assuan_transact
(
app
->
assuan_ctx
,
line
,
membuf_data_cb
,
&
data
,
NULL
,
NULL
,
NULL
,
NULL
),
"SCD PKSIGN"
);
if
(
rc
)
xfree
(
get_membuf
(
&
data
,
&
len
));
else
*
r_buf
=
get_membuf
(
&
data
,
r_buflen
);
}
}
else
{
/* Check that the card's serialnumber is as required.*/
rc
=
check_card_serialno
(
app
,
serialno
);
if
(
rc
==
-1
)
goto
retry
;
if
(
!
rc
)
rc
=
app
->
fnc
.
sign
(
app
,
serialno
,
hashalgo
,
pin_cb
,
&
parm
,
indata
,
indatalen
,
r_buf
,
r_buflen
);
}
if
(
rc
)
{
status_sc_op_failure
(
rc
);
if
(
!
app
->
assuan_ctx
)
agent_clear_pin_cache
(
serialno
);
}
return
rc
;
}
/* Send a PKDECRYPT command to the SCdaemon. */
int
agent_scd_pkdecrypt
(
const
char
*
serialno
,
const
unsigned
char
*
indata
,
size_t
indatalen
,
unsigned
char
**
r_buf
,
size_t
*
r_buflen
)
{
struct
pincb_parm_s
parm
;
app_t
app
;
int
rc
;
*
r_buf
=
NULL
;
*
r_buflen
=
0
;
memset
(
&
parm
,
0
,
sizeof
parm
);
parm
.
sn
=
serialno
;
retry
:
app
=
current_app
?
current_app
:
open_card
();
if
(
!
app
)
return
gpg_error
(
GPG_ERR_CARD
);
if
(
app
->
assuan_ctx
)
{
char
*
p
,
line
[
ASSUAN_LINELENGTH
];
membuf_t
data
;
size_t
len
;
int
i
;
if
(
indatalen
*
2
+
50
>
DIM
(
line
))
return
gpg_error
(
GPG_ERR_GENERAL
);
p
=
stpcpy
(
line
,
"SCD SETDATA "
);
for
(
i
=
0
;
i
<
indatalen
;
i
++
,
p
+=
2
)
sprintf
(
p
,
"%02X"
,
indata
[
i
]);
rc
=
test_transact
(
assuan_transact
(
app
->
assuan_ctx
,
line
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
),
"SCD SETDATA"
);
if
(
!
rc
)
{
init_membuf
(
&
data
,
1024
);
snprintf
(
line
,
DIM
(
line
)
-1
,
"SCD PKDECRYPT %s"
,
serialno
);
line
[
DIM
(
line
)
-1
]
=
0
;
rc
=
test_transact
(
assuan_transact
(
app
->
assuan_ctx
,
line
,
membuf_data_cb
,
&
data
,
NULL
,
NULL
,
NULL
,
NULL
),
"SCD PKDECRYPT"
);
if
(
rc
)
xfree
(
get_membuf
(
&
data
,
&
len
));
else
*
r_buf
=
get_membuf
(
&
data
,
r_buflen
);
}
}
else
{
/* Check that the card's serialnumber is as required.*/
rc
=
check_card_serialno
(
app
,
serialno
);
if
(
rc
==
-1
)
goto
retry
;
if
(
!
rc
)
rc
=
app
->
fnc
.
decipher
(
app
,
serialno
,
pin_cb
,
&
parm
,
indata
,
indatalen
,
r_buf
,
r_buflen
);
}
if
(
rc
)
{
status_sc_op_failure
(
rc
);
if
(
!
app
->
assuan_ctx
)
agent_clear_pin_cache
(
serialno
);
}
return
rc
;
}
/* Change the PIN of an OpenPGP card or reset the retry
counter. SERIALNO may be NULL or a hex string finally passed to the
passphrase callback. */
int
agent_scd_change_pin
(
int
chvno
,
const
char
*
serialno
)
{
app_t
app
;
int
reset
=
0
;
int
rc
;
struct
pincb_parm_s
parm
;
memset
(
&
parm
,
0
,
sizeof
parm
);
parm
.
sn
=
serialno
;
reset
=
(
chvno
>=
100
);
chvno
%=
100
;
app
=
current_app
?
current_app
:
open_card
();
if
(
!
app
)
return
gpg_error
(
GPG_ERR_CARD
);
if
(
app
->
assuan_ctx
)
{
char
line
[
ASSUAN_LINELENGTH
];
snprintf
(
line
,
DIM
(
line
)
-1
,
"SCD PASSWD%s %d"
,
reset
?
" --reset"
:
""
,
chvno
);
line
[
DIM
(
line
)
-1
]
=
0
;
rc
=
test_transact
(
assuan_transact
(
app
->
assuan_ctx
,
line
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
),
"SCD PASSWD"
);
}
else
{
char
chvnostr
[
50
];
sprintf
(
chvnostr
,
"%d"
,
chvno
);
rc
=
app
->
fnc
.
change_pin
(
app
,
NULL
,
chvnostr
,
reset
,
pin_cb
,
&
parm
);
}
status_sc_op_failure
(
rc
);
return
rc
;
}
/* Perform a CHECKPIN operation. SERIALNO should be the serial
number of the card - optionally followed by the fingerprint;
however the fingerprint is ignored here. */
int
agent_scd_checkpin
(
const
char
*
serialnobuf
)
{
app_t
app
;
int
rc
;
struct
pincb_parm_s
parm
;
memset
(
&
parm
,
0
,
sizeof
parm
);
parm
.
sn
=
serialnobuf
;
app
=
current_app
?
current_app
:
open_card
();
if
(
!
app
)
return
gpg_error
(
GPG_ERR_CARD
);
if
(
app
->
assuan_ctx
)
{
char
line
[
ASSUAN_LINELENGTH
];
if
(
15
+
strlen
(
serialnobuf
)
>
DIM
(
line
)
-1
)
return
gpg_error
(
GPG_ERR_CARD
);
stpcpy
(
stpcpy
(
line
,
"SCD CHECKPIN "
),
serialnobuf
);
rc
=
test_transact
(
assuan_transact
(
app
->
assuan_ctx
,
line
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
),
"SCD CHECKPIN"
);
}
else
{
rc
=
app
->
fnc
.
check_pin
(
app
,
serialnobuf
,
pin_cb
,
&
parm
);
}
status_sc_op_failure
(
rc
);
return
rc
;
}
void
agent_clear_pin_cache
(
const
char
*
sn
)
{
char
*
cacheid
=
format_cacheid
(
sn
);
if
(
cacheid
)
{
passphrase_clear_cache
(
NULL
,
cacheid
,
0
);
xfree
(
cacheid
);
}
}
int
agent_scd_writecert
(
const
char
*
certidstr
,
const
unsigned
char
*
certdata
,
size_t
certdatalen
)
{
/* It does not make sense to implement this rarely used and mainly
interactive command in GPG-1. GPG-2 is better suited for this. */
not_in_gpg1_notice
();
return
gpg_error
(
GPG_ERR_NOT_SUPPORTED
);
}
int
agent_scd_readcert
(
const
char
*
certidstr
,
void
**
r_buf
,
size_t
*
r_buflen
)
{
/* It does not make sense to implement this rarely used and mainly
interactive command in GPG-1. GPG-2 is better suited for this. */
*
r_buf
=
NULL
;
not_in_gpg1_notice
();
return
gpg_error
(
GPG_ERR_NOT_SUPPORTED
);
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, May 10, 8:43 AM (1 d, 20 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
5d/f4/f9212db51ba554db2eb3881a0147
Attached To
rG GnuPG
Event Timeline
Log In to Comment