agent: Extend cmd KEYINFO to return data from sshcontrol.

* agent/command-ssh.c (struct control_file_s): Rename to
ssh_control_file_s.
(ssh_open_control_file, ssh_close_control_file)
(ssh_read_control_file, ssh_search_control_file): New.
(control_file_t):  Rename and move to ...
* agent/agent.h (ssh_control_file_t): here.
* agent/command.c (do_one_keyinfo): Add args is_ssh, ttl, disabled,
and confirm. Rename unknown keytype indicator from '-' to 'X'.  Extend
output.
(cmd_keyinfo): Add options --ssh-list and --with-ssh.
--

This extension allows the development of frontends to manage the
sshcontrol file.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2013-08-08 21:22:38 +02:00
parent 498b9a95dc
commit 50c98c7ed6
3 changed files with 231 additions and 40 deletions

View File

@ -157,6 +157,10 @@ struct
/* Forward reference for local definitions in command.c. */ /* Forward reference for local definitions in command.c. */
struct server_local_s; struct server_local_s;
/* Declaration of objects from command-ssh.c. */
struct ssh_control_file_s;
typedef struct ssh_control_file_s *ssh_control_file_t;
/* Forward reference for local definitions in call-scd.c. */ /* Forward reference for local definitions in call-scd.c. */
struct scd_local_s; struct scd_local_s;
@ -290,6 +294,16 @@ gpg_error_t pinentry_loopback(ctrl_t, const char *keyword,
size_t max_length); size_t max_length);
/*-- command-ssh.c --*/ /*-- command-ssh.c --*/
ssh_control_file_t ssh_open_control_file (void);
void ssh_close_control_file (ssh_control_file_t cf);
gpg_error_t ssh_read_control_file (ssh_control_file_t cf,
char *r_hexgrip, int *r_disabled,
int *r_ttl, int *r_confirm);
gpg_error_t ssh_search_control_file (ssh_control_file_t cf,
const char *hexgrip,
int *r_disabled,
int *r_ttl, int *r_confirm);
void start_command_handler_ssh (ctrl_t, gnupg_fd_t); void start_command_handler_ssh (ctrl_t, gnupg_fd_t);
/*-- findkey.c --*/ /*-- findkey.c --*/

View File

@ -1,5 +1,6 @@
/* command-ssh.c - gpg-agent's ssh-agent emulation layer /* command-ssh.c - gpg-agent's ssh-agent emulation layer
* Copyright (C) 2004, 2005, 2006, 2009, 2012 Free Software Foundation, Inc. * Copyright (C) 2004, 2005, 2006, 2009, 2012 Free Software Foundation, Inc.
* Copyright (C) 2013 Werner Koch
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -87,7 +88,7 @@ static const char sshcontrolblurb[] =
"# the format of the entries is fixed and checked by gpg-agent. A\n" "# the format of the entries is fixed and checked by gpg-agent. A\n"
"# non-comment line starts with optional white spaces, followed by the\n" "# non-comment line starts with optional white spaces, followed by the\n"
"# keygrip of the key given as 40 hex digits, optionally followed by a\n" "# keygrip of the key given as 40 hex digits, optionally followed by a\n"
"# the caching TTL in seconds and another optional field for arbitrary\n" "# caching TTL in seconds, and another optional field for arbitrary\n"
"# flags. Prepend the keygrip with an '!' mark to disable it.\n" "# flags. Prepend the keygrip with an '!' mark to disable it.\n"
"\n"; "\n";
@ -186,8 +187,8 @@ struct ssh_key_type_spec
}; };
/* An object used to access the sshcontrol file. */ /* Definition of an object to access the sshcontrol file. */
struct control_file_s struct ssh_control_file_s
{ {
char *fname; /* Name of the file. */ char *fname; /* Name of the file. */
FILE *fp; /* This is never NULL. */ FILE *fp; /* This is never NULL. */
@ -200,8 +201,6 @@ struct control_file_s
char hexgrip[40+1]; /* The hexgrip of the item (uppercase). */ char hexgrip[40+1]; /* The hexgrip of the item (uppercase). */
} item; } item;
}; };
typedef struct control_file_s *control_file_t;
/* Prototypes. */ /* Prototypes. */
@ -730,10 +729,10 @@ file_to_buffer (const char *filename, unsigned char **buffer, size_t *buffer_n)
control file object stored at R_CF. On error an error code is control file object stored at R_CF. On error an error code is
returned and NULL is stored at R_CF. */ returned and NULL is stored at R_CF. */
static gpg_error_t static gpg_error_t
open_control_file (control_file_t *r_cf, int append) open_control_file (ssh_control_file_t *r_cf, int append)
{ {
gpg_error_t err; gpg_error_t err;
control_file_t cf; ssh_control_file_t cf;
cf = xtrycalloc (1, sizeof *cf); cf = xtrycalloc (1, sizeof *cf);
if (!cf) if (!cf)
@ -795,7 +794,7 @@ open_control_file (control_file_t *r_cf, int append)
static void static void
rewind_control_file (control_file_t cf) rewind_control_file (ssh_control_file_t cf)
{ {
fseek (cf->fp, 0, SEEK_SET); fseek (cf->fp, 0, SEEK_SET);
cf->lnr = 0; cf->lnr = 0;
@ -804,7 +803,7 @@ rewind_control_file (control_file_t cf)
static void static void
close_control_file (control_file_t cf) close_control_file (ssh_control_file_t cf)
{ {
if (!cf) if (!cf)
return; return;
@ -818,7 +817,7 @@ close_control_file (control_file_t cf)
/* Read the next line from the control file and store the data in CF. /* Read the next line from the control file and store the data in CF.
Returns 0 on success, GPG_ERR_EOF on EOF, or other error codes. */ Returns 0 on success, GPG_ERR_EOF on EOF, or other error codes. */
static gpg_error_t static gpg_error_t
read_control_file_item (control_file_t cf) read_control_file_item (ssh_control_file_t cf)
{ {
int c, i, n; int c, i, n;
char *p, *pend, line[256]; char *p, *pend, line[256];
@ -921,7 +920,7 @@ read_control_file_item (control_file_t cf)
a specified TTL for that key is stored there. If R_CONFIRM is not a specified TTL for that key is stored there. If R_CONFIRM is not
NULL it is set to 1 if the key has the confirm flag set. */ NULL it is set to 1 if the key has the confirm flag set. */
static gpg_error_t static gpg_error_t
search_control_file (control_file_t cf, const char *hexgrip, search_control_file (ssh_control_file_t cf, const char *hexgrip,
int *r_disabled, int *r_ttl, int *r_confirm) int *r_disabled, int *r_ttl, int *r_confirm)
{ {
gpg_error_t err; gpg_error_t err;
@ -965,7 +964,7 @@ add_control_entry (ctrl_t ctrl, const char *hexgrip, const char *fmtfpr,
int ttl, int confirm) int ttl, int confirm)
{ {
gpg_error_t err; gpg_error_t err;
control_file_t cf; ssh_control_file_t cf;
int disabled; int disabled;
(void)ctrl; (void)ctrl;
@ -1001,7 +1000,7 @@ add_control_entry (ctrl_t ctrl, const char *hexgrip, const char *fmtfpr,
static int static int
ttl_from_sshcontrol (const char *hexgrip) ttl_from_sshcontrol (const char *hexgrip)
{ {
control_file_t cf; ssh_control_file_t cf;
int disabled, ttl; int disabled, ttl;
if (!hexgrip || strlen (hexgrip) != 40) if (!hexgrip || strlen (hexgrip) != 40)
@ -1024,7 +1023,7 @@ ttl_from_sshcontrol (const char *hexgrip)
static int static int
confirm_flag_from_sshcontrol (const char *hexgrip) confirm_flag_from_sshcontrol (const char *hexgrip)
{ {
control_file_t cf; ssh_control_file_t cf;
int disabled, confirm; int disabled, confirm;
if (!hexgrip || strlen (hexgrip) != 40) if (!hexgrip || strlen (hexgrip) != 40)
@ -1044,6 +1043,87 @@ confirm_flag_from_sshcontrol (const char *hexgrip)
} }
/* Open the ssh control file for reading. This is a public version of
open_control_file. The caller must use ssh_close_control_file to
release the retruned handle. */
ssh_control_file_t
ssh_open_control_file (void)
{
ssh_control_file_t cf;
/* Then look at all the registered and non-disabled keys. */
if (open_control_file (&cf, 0))
return NULL;
return cf;
}
/* Close an ssh control file handle. This is the public version of
close_control_file. CF may be NULL. */
void
ssh_close_control_file (ssh_control_file_t cf)
{
close_control_file (cf);
}
/* Read the next item from the ssh control file. The function returns
0 if a item was read, GPG_ERR_EOF on eof or another error value.
R_HEXGRIP shall either be null or a BUFFER of at least 41 byte.
R_DISABLED, R_TTLm and R_CONFIRM return flags from the control
file; they are only set on success. */
gpg_error_t
ssh_read_control_file (ssh_control_file_t cf,
char *r_hexgrip,
int *r_disabled, int *r_ttl, int *r_confirm)
{
gpg_error_t err;
do
err = read_control_file_item (cf);
while (!err && !cf->item.valid);
if (!err)
{
if (r_hexgrip)
strcpy (r_hexgrip, cf->item.hexgrip);
if (r_disabled)
*r_disabled = cf->item.disabled;
if (r_ttl)
*r_ttl = cf->item.ttl;
if (r_confirm)
*r_confirm = cf->item.confirm;
}
return err;
}
/* Search for a key with HEXGRIP in sshcontrol and return all
info. */
gpg_error_t
ssh_search_control_file (ssh_control_file_t cf,
const char *hexgrip,
int *r_disabled, int *r_ttl, int *r_confirm)
{
gpg_error_t err;
int i;
const char *s;
char uphexgrip[41];
/* We need to make sure that HEXGRIP is all uppercase. The easiest
way to do this and also check its length is by copying to a
second buffer. */
for (i=0, s=hexgrip; i < 40; s++, i++)
uphexgrip[i] = *s >= 'a'? (*s & 0xdf): *s;
uphexgrip[i] = 0;
if (i != 40)
err = gpg_error (GPG_ERR_INV_LENGTH);
else
err = search_control_file (cf, uphexgrip, r_disabled, r_ttl, r_confirm);
if (gpg_err_code (err) == GPG_ERR_EOF)
err = gpg_error (GPG_ERR_NOT_FOUND);
return err;
}
@ -2185,7 +2265,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
gcry_sexp_t key_public; gcry_sexp_t key_public;
gpg_error_t err; gpg_error_t err;
int ret; int ret;
control_file_t cf = NULL; ssh_control_file_t cf = NULL;
char *cardsn; char *cardsn;
gpg_error_t ret_err; gpg_error_t ret_err;

View File

@ -998,13 +998,15 @@ cmd_readkey (assuan_context_t ctx, char *line)
static const char hlp_keyinfo[] = static const char hlp_keyinfo[] =
"KEYINFO [--list] [--data] [--ssh-fpr] <keygrip>\n" "KEYINFO [--[ssh-]list] [--data] [--ssh-fpr] [--with-ssh] <keygrip>\n"
"\n" "\n"
"Return information about the key specified by the KEYGRIP. If the\n" "Return information about the key specified by the KEYGRIP. If the\n"
"key is not available GPG_ERR_NOT_FOUND is returned. If the option\n" "key is not available GPG_ERR_NOT_FOUND is returned. If the option\n"
"--list is given the keygrip is ignored and information about all\n" "--list is given the keygrip is ignored and information about all\n"
"available keys are returned. The information is returned as a\n" "available keys are returned. If --ssh-list is given information\n"
"status line unless --data was specified, with this format:\n" "about all keys listed in the sshcontrol are returned. With --with-ssh\n"
"information from sshcontrol is always added to the info. Unless --data\n"
"is given, the information is returned as a status line using the format:\n"
"\n" "\n"
" KEYINFO <keygrip> <type> <serialno> <idstr> <cached> <protection> <fpr>\n" " KEYINFO <keygrip> <type> <serialno> <idstr> <cached> <protection> <fpr>\n"
"\n" "\n"
@ -1013,7 +1015,8 @@ static const char hlp_keyinfo[] =
"TYPE is describes the type of the key:\n" "TYPE is describes the type of the key:\n"
" 'D' - Regular key stored on disk,\n" " 'D' - Regular key stored on disk,\n"
" 'T' - Key is stored on a smartcard (token),\n" " 'T' - Key is stored on a smartcard (token),\n"
" '-' - Unknown type.\n" " 'X' - Unknown type,\n"
" '-' - Key is missing.\n"
"\n" "\n"
"SERIALNO is an ASCII string with the serial number of the\n" "SERIALNO is an ASCII string with the serial number of the\n"
" smartcard. If the serial number is not known a single\n" " smartcard. If the serial number is not known a single\n"
@ -1031,13 +1034,21 @@ static const char hlp_keyinfo[] =
" '-' - Unknown protection.\n" " '-' - Unknown protection.\n"
"\n" "\n"
"FPR returns the formatted ssh-style fingerprint of the key. It is only\n" "FPR returns the formatted ssh-style fingerprint of the key. It is only\n"
" print if the option --ssh-fpr has been used. '-' is printed if the\n" " printed if the option --ssh-fpr has been used. It defaults to '-'.\n"
" fingerprint is not available.\n" "\n"
"TTL is the TTL in seconds for that key or '-' if n/a.\n"
"\n"
"FLAGS is a word consisting of one-letter flags:\n"
" 'D' - The key has been disabled,\n"
" 'S' - The key is listed in sshcontrol (requires --with-ssh),\n"
" 'c' - Use of the key needs to be confirmed,\n"
" '-' - No flags given.\n"
"\n" "\n"
"More information may be added in the future."; "More information may be added in the future.";
static gpg_error_t static gpg_error_t
do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx, do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx,
int data, int with_ssh_fpr) int data, int with_ssh_fpr, int in_ssh,
int ttl, int disabled, int confirm)
{ {
gpg_error_t err; gpg_error_t err;
char hexgrip[40+1]; char hexgrip[40+1];
@ -1050,24 +1061,55 @@ do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx,
const char *cached; const char *cached;
const char *protectionstr; const char *protectionstr;
char *pw; char *pw;
int missing_key = 0;
char ttlbuf[20];
char flagsbuf[5];
err = agent_key_info_from_file (ctrl, grip, &keytype, &shadow_info); err = agent_key_info_from_file (ctrl, grip, &keytype, &shadow_info);
if (err) if (err)
goto leave; {
if (in_ssh && gpg_err_code (err) == GPG_ERR_NOT_FOUND)
missing_key = 1;
else
goto leave;
}
/* Reformat the grip so that we use uppercase as good style. */ /* Reformat the grip so that we use uppercase as good style. */
bin2hex (grip, 20, hexgrip); bin2hex (grip, 20, hexgrip);
switch (keytype) if (ttl > 0)
snprintf (ttlbuf, sizeof ttlbuf, "%d", ttl);
else
strcpy (ttlbuf, "-");
*flagsbuf = 0;
if (disabled)
strcat (flagsbuf, "D");
if (in_ssh)
strcat (flagsbuf, "S");
if (confirm)
strcat (flagsbuf, "c");
if (!*flagsbuf)
strcpy (flagsbuf, "-");
if (missing_key)
{ {
case PRIVATE_KEY_CLEAR: protectionstr = "C"; keytypestr = "D"; protectionstr = "-"; keytypestr = "-";
break; }
case PRIVATE_KEY_PROTECTED: protectionstr = "P"; keytypestr = "D"; else
break; {
case PRIVATE_KEY_SHADOWED: protectionstr = "-"; keytypestr = "T"; switch (keytype)
break; {
default: protectionstr = "-"; keytypestr = "-"; case PRIVATE_KEY_CLEAR: protectionstr = "C"; keytypestr = "D";
break; break;
case PRIVATE_KEY_PROTECTED: protectionstr = "P"; keytypestr = "D";
break;
case PRIVATE_KEY_SHADOWED: protectionstr = "-"; keytypestr = "T";
break;
default: protectionstr = "-"; keytypestr = "X";
break;
}
} }
/* Compute the ssh fingerprint if requested. */ /* Compute the ssh fingerprint if requested. */
@ -1105,16 +1147,20 @@ do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx,
cached, cached,
protectionstr, protectionstr,
fpr? fpr : "-", fpr? fpr : "-",
ttlbuf,
flagsbuf,
NULL); NULL);
else else
{ {
char *string; char *string;
string = xtryasprintf ("%s %s %s %s %s %s %s\n", string = xtryasprintf ("%s %s %s %s %s %s %s %s %s\n",
hexgrip, keytypestr, hexgrip, keytypestr,
serialno? serialno : "-", serialno? serialno : "-",
idstr? idstr : "-", cached, protectionstr, idstr? idstr : "-", cached, protectionstr,
fpr? fpr : "-"); fpr? fpr : "-",
ttlbuf,
flagsbuf);
if (!string) if (!string)
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
else else
@ -1141,18 +1187,44 @@ cmd_keyinfo (assuan_context_t ctx, char *line)
unsigned char grip[20]; unsigned char grip[20];
DIR *dir = NULL; DIR *dir = NULL;
int list_mode; int list_mode;
int opt_data, opt_ssh_fpr; int opt_data, opt_ssh_fpr, opt_with_ssh;
ssh_control_file_t cf = NULL;
char hexgrip[41];
int disabled, ttl, confirm, is_ssh;
list_mode = has_option (line, "--list"); if (has_option (line, "--ssh-list"))
list_mode = 2;
else
list_mode = has_option (line, "--list");
opt_data = has_option (line, "--data"); opt_data = has_option (line, "--data");
opt_ssh_fpr = has_option (line, "--ssh-fpr"); opt_ssh_fpr = has_option (line, "--ssh-fpr");
opt_with_ssh = has_option (line, "--with-ssh");
line = skip_options (line); line = skip_options (line);
if (list_mode) if (opt_with_ssh || list_mode == 2)
cf = ssh_open_control_file ();
if (list_mode == 2)
{
if (cf)
{
while (!ssh_read_control_file (cf, hexgrip,
&disabled, &ttl, &confirm))
{
if (hex2bin (hexgrip, grip, 20) < 0 )
continue; /* Bad hex string. */
err = do_one_keyinfo (ctrl, grip, ctx, opt_data, opt_ssh_fpr, 1,
ttl, disabled, confirm);
if (err)
goto leave;
}
}
err = 0;
}
else if (list_mode)
{ {
char *dirname; char *dirname;
struct dirent *dir_entry; struct dirent *dir_entry;
char hexgrip[41];
dirname = make_filename_try (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, NULL); dirname = make_filename_try (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, NULL);
if (!dirname) if (!dirname)
@ -1180,7 +1252,19 @@ cmd_keyinfo (assuan_context_t ctx, char *line)
if ( hex2bin (hexgrip, grip, 20) < 0 ) if ( hex2bin (hexgrip, grip, 20) < 0 )
continue; /* Bad hex string. */ continue; /* Bad hex string. */
err = do_one_keyinfo (ctrl, grip, ctx, opt_data, opt_ssh_fpr); disabled = ttl = confirm = is_ssh = 0;
if (opt_with_ssh)
{
err = ssh_search_control_file (cf, hexgrip,
&disabled, &ttl, &confirm);
if (!err)
is_ssh = 1;
else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND)
goto leave;
}
err = do_one_keyinfo (ctrl, grip, ctx, opt_data, opt_ssh_fpr, is_ssh,
ttl, disabled, confirm);
if (err) if (err)
goto leave; goto leave;
} }
@ -1191,10 +1275,23 @@ cmd_keyinfo (assuan_context_t ctx, char *line)
err = parse_keygrip (ctx, line, grip); err = parse_keygrip (ctx, line, grip);
if (err) if (err)
goto leave; goto leave;
err = do_one_keyinfo (ctrl, grip, ctx, opt_data, opt_ssh_fpr); disabled = ttl = confirm = is_ssh = 0;
if (opt_with_ssh)
{
err = ssh_search_control_file (cf, line,
&disabled, &ttl, &confirm);
if (!err)
is_ssh = 1;
else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND)
goto leave;
}
err = do_one_keyinfo (ctrl, grip, ctx, opt_data, opt_ssh_fpr, is_ssh,
ttl, disabled, confirm);
} }
leave: leave:
ssh_close_control_file (cf);
if (dir) if (dir)
closedir (dir); closedir (dir);
if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND) if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND)