From 4e5bf2fd93a175f64aa1ca2e4b35dcf853f7f828 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 23 Feb 2005 21:06:32 +0000 Subject: * command-ssh.c (get_passphrase): Removed. (ssh_identity_register): Partly rewritten. (open_control_file, search_control_file, add_control_entry): New. (ssh_handler_request_identities): Return only files listed in our control file. * findkey.c (unprotect): Check for allocation error. * agent.h (opt): Add fields to record the startup terminal settings. * gpg-agent.c (main): Record them and do not force keep display with --enable-ssh-support. * command-ssh.c (start_command_handler_ssh): Use them here. * gpg-agent.c: Renamed option --ssh-support to --enable-ssh-support. * command.c (cmd_readkey): New. (register_commands): Register new command "READKEY". * command-ssh.c (ssh_request_process): Improved logging. * findkey.c (agent_write_private_key): Always use plain open. Don't depend on an umask for permissions. (agent_key_from_file): Factored file reading code out to .. (read_key_file): .. new function. (agent_public_key_from_file): New. --- agent/findkey.c | 330 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 274 insertions(+), 56 deletions(-) (limited to 'agent/findkey.c') diff --git a/agent/findkey.c b/agent/findkey.c index 1ac57ad07..86a28d511 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -39,7 +39,9 @@ struct try_unprotect_arg_s { }; - +/* Write an S-expression formatted key to our key storage. With FORCE + pased as true an existsing key with the given GRIP will get + overwritten. */ int agent_write_private_key (const unsigned char *grip, const void *buffer, size_t length, int force) @@ -48,51 +50,44 @@ agent_write_private_key (const unsigned char *grip, char *fname; FILE *fp; char hexgrip[40+4+1]; + int fd; for (i=0; i < 20; i++) sprintf (hexgrip+2*i, "%02X", grip[i]); strcpy (hexgrip+40, ".key"); fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); - if (force) - fp = fopen (fname, "wb"); - else - { - int fd; - - if (!access (fname, F_OK)) - { - log_error ("secret key file `%s' already exists\n", fname); - xfree (fname); - return gpg_error (GPG_ERR_GENERAL); - } - /* We would like to create FNAME but only if it does not already - exist. We cannot make this guarantee just using POSIX (GNU - provides the "x" opentype for fopen, however, this is not - portable). Thus, we use the more flexible open function and - then use fdopen to obtain a stream. + if (!force && !access (fname, F_OK)) + { + log_error ("secret key file `%s' already exists\n", fname); + xfree (fname); + return gpg_error (GPG_ERR_GENERAL); + } - The mode parameter to open is what fopen uses. It will be - combined with the process' umask automatically. */ - fd = open (fname, O_CREAT | O_EXCL | O_RDWR, - S_IRUSR | S_IWUSR + /* In FORCE mode we would like to create FNAME but only if it does + not already exist. We cannot make this guarantee just using + POSIX (GNU provides the "x" opentype for fopen, however, this is + not portable). Thus, we use the more flexible open function and + then use fdopen to obtain a stream. */ + fd = open (fname, force? (O_CREAT | O_TRUNC | O_WRONLY) + : (O_CREAT | O_EXCL | O_WRONLY), + S_IRUSR | S_IWUSR #ifndef HAVE_W32_SYSTEM - | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH + | S_IRGRP #endif ); - if (fd < 0) - fp = 0; - else - { - fp = fdopen (fd, "wb"); - if (!fp) - { - int save_e = errno; - close (fd); - errno = save_e; - } - } + if (fd < 0) + fp = NULL; + else + { + fp = fdopen (fd, "wb"); + if (!fp) + { + int save_e = errno; + close (fd); + errno = save_e; + } } if (!fp) @@ -263,6 +258,8 @@ unprotect (CTRL ctrl, const char *desc_text, } pi = gcry_calloc_secure (1, sizeof (*pi) + 100); + if (!pi) + return gpg_error_from_errno (errno); pi->max_length = 100; pi->min_digits = 0; /* we want a real passphrase */ pi->max_digits = 8; @@ -285,32 +282,22 @@ unprotect (CTRL ctrl, const char *desc_text, } - -/* Return the secret key as an S-Exp in RESULT after locating it using - the grip. Returns NULL in RESULT if the operation should be - diverted to a token; SHADOW_INFO will point then to an allocated - S-Expression with the shadow_info part from the file. With - IGNORE_CACHE passed as true the passphrase is not taken from the - cache. DESC_TEXT may be set to present a custom description for the - pinentry. */ -gpg_error_t -agent_key_from_file (CTRL ctrl, const char *desc_text, - const unsigned char *grip, unsigned char **shadow_info, - int ignore_cache, gcry_sexp_t *result) +/* Read the key identified by GRIP from the private key directory and + return it as an gcrypt S-expression object in RESULT. On failure + returns an error code and stores NULL at RESULT. */ +static gpg_error_t +read_key_file (const unsigned char *grip, gcry_sexp_t *result) { int i, rc; char *fname; FILE *fp; struct stat st; unsigned char *buf; - size_t len, buflen, erroff; + size_t buflen, erroff; gcry_sexp_t s_skey; char hexgrip[40+4+1]; - int got_shadow_info = 0; *result = NULL; - if (shadow_info) - *shadow_info = NULL; for (i=0; i < 20; i++) sprintf (hexgrip+2*i, "%02X", grip[i]); @@ -336,8 +323,8 @@ agent_key_from_file (CTRL ctrl, const char *desc_text, } buflen = st.st_size; - buf = xmalloc (buflen+1); - if (fread (buf, buflen, 1, fp) != 1) + buf = xtrymalloc (buflen+1); + if (!buf || fread (buf, buflen, 1, fp) != 1) { rc = gpg_error_from_errno (errno); log_error ("error reading `%s': %s\n", fname, strerror (errno)); @@ -347,6 +334,7 @@ agent_key_from_file (CTRL ctrl, const char *desc_text, return rc; } + /* Convert the file into a gcrypt S-expression object. */ rc = gcry_sexp_sscan (&s_skey, &erroff, buf, buflen); xfree (fname); fclose (fp); @@ -357,18 +345,52 @@ agent_key_from_file (CTRL ctrl, const char *desc_text, (unsigned int)erroff, gpg_strerror (rc)); return rc; } + *result = s_skey; + return 0; +} + + +/* Return the secret key as an S-Exp in RESULT after locating it using + the grip. Returns NULL in RESULT if the operation should be + diverted to a token; SHADOW_INFO will point then to an allocated + S-Expression with the shadow_info part from the file. With + IGNORE_CACHE passed as true the passphrase is not taken from the + cache. DESC_TEXT may be set to present a custom description for the + pinentry. */ +gpg_error_t +agent_key_from_file (ctrl_t ctrl, const char *desc_text, + const unsigned char *grip, unsigned char **shadow_info, + int ignore_cache, gcry_sexp_t *result) +{ + int rc; + unsigned char *buf; + size_t len, buflen, erroff; + gcry_sexp_t s_skey; + int got_shadow_info = 0; + + *result = NULL; + if (shadow_info) + *shadow_info = NULL; + + rc = read_key_file (grip, &s_skey); + if (rc) + return rc; + + /* For use with the protection functions we also need the key as an + canonical encoded S-expression in abuffer. Create this buffer + now. */ len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0); assert (len); buf = xtrymalloc (len); if (!buf) { - rc = out_of_core (); + rc = gpg_error_from_errno (errno); gcry_sexp_release (s_skey); return rc; } len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, buf, len); assert (len); - gcry_sexp_release (s_skey); + switch (agent_private_key_type (buf)) { @@ -381,7 +403,7 @@ agent_key_from_file (CTRL ctrl, const char *desc_text, char *desc_text_final; const char *comment = NULL; - /* Note, that we will take the comment as a C styring for + /* Note, that we will take the comment as a C string for display purposes; i.e. all stuff beyond a Nul character is ignored. */ comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0); @@ -460,6 +482,8 @@ agent_key_from_file (CTRL ctrl, const char *desc_text, rc = gpg_error (GPG_ERR_BAD_SECKEY); break; } + gcry_sexp_release (s_skey); + s_skey = NULL; if (rc || got_shadow_info) { xfree (buf); @@ -481,6 +505,200 @@ agent_key_from_file (CTRL ctrl, const char *desc_text, return 0; } + + +/* Return the public key for the keygrip GRIP. The result is stored + at RESULT. This function extracts the public key from the private + key database. On failure an error code is returned and NULL stored + at RESULT. */ +gpg_error_t +agent_public_key_from_file (ctrl_t ctrl, + const unsigned char *grip, + gcry_sexp_t *result) +{ + int i, idx, rc; + gcry_sexp_t s_skey; + const char *algoname; + gcry_sexp_t uri_sexp, comment_sexp; + const char *uri, *comment; + size_t uri_length, comment_length; + char *format, *p; + void *args[4+2+2+1]; /* Size is max. # of elements + 2 for uri + 2 + for comment + end-of-list. */ + int argidx; + gcry_sexp_t list, l2; + const char *name; + const char *s; + size_t n; + const char *elems; + gcry_mpi_t *array; + + *result = NULL; + + rc = read_key_file (grip, &s_skey); + if (rc) + return rc; + + list = gcry_sexp_find_token (s_skey, "shadowed-private-key", 0 ); + if (!list) + list = gcry_sexp_find_token (s_skey, "protected-private-key", 0 ); + if (!list) + list = gcry_sexp_find_token (s_skey, "private-key", 0 ); + if (!list) + { + log_error ("invalid private key format\n"); + gcry_sexp_release (s_skey); + return gpg_error (GPG_ERR_BAD_SECKEY); + } + + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + name = gcry_sexp_nth_data (list, 0, &n); + if (n==3 && !memcmp (name, "rsa", 3)) + { + algoname = "rsa"; + elems = "ne"; + } + else if (n==3 && !memcmp (name, "dsa", 3)) + { + algoname = "dsa"; + elems = "pqgy"; + } + else if (n==3 && !memcmp (name, "elg", 3)) + { + algoname = "elg"; + elems = "pgy"; + } + else + { + log_error ("unknown private key algorithm\n"); + gcry_sexp_release (list); + gcry_sexp_release (s_skey); + return gpg_error (GPG_ERR_BAD_SECKEY); + } + + /* Allocate an array for the parameters and copy them out of the + secret key. FIXME: We should have a generic copy function. */ + array = xtrycalloc (strlen(elems) + 1, sizeof *array); + if (!array) + { + rc = gpg_error_from_errno (errno); + gcry_sexp_release (list); + gcry_sexp_release (s_skey); + return rc; + } + + for (idx=0, s=elems; *s; s++, idx++ ) + { + l2 = gcry_sexp_find_token (list, s, 1); + if (!l2) + { + /* Required parameter not found. */ + for (i=0; i