diff --git a/AUTHORS b/AUTHORS index df4e1ec39..65522a1cc 100644 --- a/AUTHORS +++ b/AUTHORS @@ -221,7 +221,7 @@ Kyle Butt 2013-05-29:CAAODAYLbCtqOG6msLLL0UTdASKWT6u2ptxsgUQ1JpusBESBoNQ@mail.gmail.com: Mario Haustein -2022-09026:8149069.T7Z3S40VBb@localdomain: +2022-09-26:8149069.T7Z3S40VBb@localdomain: Michael Haubenwallner 2018-07-13:c397e637-f1ce-34f0-7e6a-df04a76e1c35@ssi-schaefer.com: diff --git a/NEWS b/NEWS index f38cf9f0d..b9bff6232 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,9 @@ Noteworthy changes in version 2.5.0 (unreleased) ------------------------------------------------ - Changes also found in 2.4.3: + Changes also found in 2.4.4: + + * gpgsm: Support ECDSA in de-vs compliance mode. [T6802] * Fix garbled time output in non-English Windows. [T6741] diff --git a/README b/README index aa8b3e12b..6905daafb 100644 --- a/README +++ b/README @@ -144,6 +144,13 @@ gpg --import --import-options restore < allkeys.gpg gpgsm --import < allcerts.crt + In case the keyboxd is not able to startup due to a stale lockfile + created by another host, the command + + gpgconf --unlock pubring.db + + can be used to remove the lock file. + ** Socket directory GnuPG uses Unix domain sockets to connect its components (on Windows @@ -166,6 +173,29 @@ fi done ) +** Conflicts with systemd socket activation + + Some Linux distribution use the meanwhile deprecated --supervised + option with gpg-agent, dirmngr, and keyboxd. The idea is that the + systemd process launches the daemons as soon as gpg or gpgsm try to + access them. However, this creates a race condition with GnuPG's + own on-demand launching of these daemon. It also conflicts with the + remote use gpg-agent because the no-autostart feature on the remote + site will not work as expected. + + Thus the recommendation is not to use the --supervised option. All + GnuPG components handle the startup of their daemons on their own. + + The only problem is that for using GnuPG's ssh-agent protocol + support, the gpg-agent must have been started before ssh. This can + either be done with an ssh wrapper running + + gpg-connect-agent updatestartuptty /bye + + for each new tty or by using that command directly after login when + the anyway required SSH_AUTH_SOCK envvar is set (see the example in + the gpg-agent man page). + * DOCUMENTATION diff --git a/agent/findkey.c b/agent/findkey.c index a5f022574..4e55119e3 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -41,7 +41,8 @@ static gpg_error_t read_key_file (const unsigned char *grip, - gcry_sexp_t *result, nvc_t *r_keymeta); + gcry_sexp_t *result, nvc_t *r_keymeta, + char **r_orig_key_value); static gpg_error_t is_shadowed_key (gcry_sexp_t s_skey); @@ -129,12 +130,15 @@ agent_write_private_key (const unsigned char *grip, char **tokenfields = NULL; int is_regular; int blocksigs = 0; + char *orig_key_value = NULL; + const char *s; + int force_modify = 0; fname = fname_from_keygrip (grip, 0); if (!fname) return gpg_error_from_syserror (); - err = read_key_file (grip, &key, &pk); + err = read_key_file (grip, &key, &pk, &orig_key_value); if (err) { if (gpg_err_code (err) == GPG_ERR_ENOENT) @@ -146,6 +150,8 @@ agent_write_private_key (const unsigned char *grip, } } + nvc_modified (pk, 1); /* Clear that flag after a read. */ + if (!pk) { /* Key is still in the old format or does not exist - create a @@ -156,6 +162,7 @@ agent_write_private_key (const unsigned char *grip, err = gpg_error_from_syserror (); goto leave; } + force_modify = 1; } /* Check whether we already have a regular key. */ @@ -171,6 +178,19 @@ agent_write_private_key (const unsigned char *grip, if (err) goto leave; + /* Detect whether the key value actually changed and if not clear + * the modified flag. This extra check is required because + * read_key_file removes the Key entry from the container and we + * then create a new Key entry which might be the same, though. */ + if (!force_modify + && orig_key_value && (s = nvc_get_string (pk, "Key:")) + && !strcmp (orig_key_value, s)) + { + nvc_modified (pk, 1); /* Clear that flag. */ + } + xfree (orig_key_value); + orig_key_value = NULL; + /* Check that we do not update a regular key with a shadow key. */ if (is_regular && gpg_err_code (is_shadowed_key (key)) == GPG_ERR_TRUE) { @@ -192,7 +212,6 @@ agent_write_private_key (const unsigned char *grip, if (serialno && keyref) { nve_t item; - const char *s; size_t token0len; if (dispserialno) @@ -242,7 +261,7 @@ agent_write_private_key (const unsigned char *grip, ; /* No need to update Token entry. */ else { - err = nve_set (item, token); + err = nve_set (pk, item, token); if (err) goto leave; } @@ -263,6 +282,13 @@ agent_write_private_key (const unsigned char *grip, goto leave; } + /* Check whether we need to write the file at all. */ + if (!nvc_modified (pk, 0)) + { + err = 0; + goto leave; + } + /* Create a temporary file for writing. */ tmpfname = fname_from_keygrip (grip, 1); fp = tmpfname ? es_fopen (tmpfname, "wbx,mode=-rw") : NULL; @@ -310,6 +336,7 @@ agent_write_private_key (const unsigned char *grip, es_fclose (fp); if (removetmp && tmpfname) gnupg_remove (tmpfname); + xfree (orig_key_value); xfree (fname); xfree (tmpfname); xfree (token); @@ -856,10 +883,13 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, * return it as an gcrypt S-expression object in RESULT. If R_KEYMETA * is not NULL and the extended key format is used, the meta data * items are stored there. However the "Key:" item is removed from - * it. On failure returns an error code and stores NULL at RESULT and - * R_KEYMETA. */ + * it. If R_ORIG_KEY_VALUE is non-NULL and the Key item was removed, + * its original value is stored at that R_ORIG_KEY_VALUE and the + * caller must free it. On failure returns an error code and stores + * NULL at RESULT and R_KEYMETA. */ static gpg_error_t -read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta) +read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta, + char **r_orig_key_value) { gpg_error_t err; char *fname; @@ -873,6 +903,8 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta) *result = NULL; if (r_keymeta) *r_keymeta = NULL; + if (r_orig_key_value) + *r_orig_key_value = NULL; fname = fname_from_keygrip (grip, 0); if (!fname) @@ -927,7 +959,24 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta) log_error ("error getting private key from '%s': %s\n", fname, gpg_strerror (err)); else - nvc_delete_named (pk, "Key:"); + { + if (r_orig_key_value) + { + const char *s = nvc_get_string (pk, "Key:"); + if (s) + { + *r_orig_key_value = xtrystrdup (s); + if (!*r_orig_key_value) + { + err = gpg_error_from_syserror (); + nvc_release (pk); + xfree (fname); + return err; + } + } + } + nvc_delete_named (pk, "Key:"); + } } if (!err && r_keymeta) @@ -1177,7 +1226,7 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, if (!grip && !ctrl->have_keygrip) return gpg_error (GPG_ERR_NO_SECKEY); - err = read_key_file (grip? grip : ctrl->keygrip, &s_skey, &keymeta); + err = read_key_file (grip? grip : ctrl->keygrip, &s_skey, &keymeta, NULL); if (err) { if (gpg_err_code (err) == GPG_ERR_ENOENT) @@ -1436,7 +1485,7 @@ agent_raw_key_from_file (ctrl_t ctrl, const unsigned char *grip, *result = NULL; - err = read_key_file (grip, &s_skey, r_keymeta); + err = read_key_file (grip, &s_skey, r_keymeta, NULL); if (!err) *result = s_skey; return err; @@ -1479,7 +1528,7 @@ public_key_from_file (ctrl_t ctrl, const unsigned char *grip, if (r_sshorder) *r_sshorder = 0; - err = read_key_file (grip, &s_skey, for_ssh? &keymeta : NULL); + err = read_key_file (grip, &s_skey, for_ssh? &keymeta : NULL, NULL); if (err) return err; @@ -1651,7 +1700,7 @@ agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip, { gcry_sexp_t sexp; - err = read_key_file (grip, &sexp, NULL); + err = read_key_file (grip, &sexp, NULL, NULL); if (err) { if (gpg_err_code (err) == GPG_ERR_ENOENT) @@ -1735,7 +1784,7 @@ agent_delete_key (ctrl_t ctrl, const char *desc_text, char *default_desc = NULL; int key_type; - err = read_key_file (grip, &s_skey, NULL); + err = read_key_file (grip, &s_skey, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_ENOENT) err = gpg_error (GPG_ERR_NO_SECKEY); if (err) diff --git a/common/compliance.c b/common/compliance.c index 59d94038d..04978ed1b 100644 --- a/common/compliance.c +++ b/common/compliance.c @@ -256,6 +256,13 @@ gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance, if (! initialized) return 1; + /* Map the the generic ECC algo to ECDSA if requested. */ + if ((algo_flags & PK_ALGO_FLAG_ECC18) + && algo == GCRY_PK_ECC + && (use == PK_USE_VERIFICATION + || use == PK_USE_SIGNING)) + algo = GCRY_PK_ECDSA; + switch (compliance) { case CO_DE_VS: @@ -280,7 +287,6 @@ gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance, default: log_assert (!"reached"); } - (void)algo_flags; break; case PUBKEY_ALGO_DSA: @@ -301,7 +307,7 @@ gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance, result = (use == PK_USE_DECRYPTION); break; - case PUBKEY_ALGO_ECDH: + case PUBKEY_ALGO_ECDH: /* Same value as GCRY_PK_ECC, i.e. 18 */ case GCRY_PK_ECDH: if (use == PK_USE_DECRYPTION) result = 1; @@ -549,6 +555,9 @@ gnupg_rng_is_compliant (enum gnupg_compliance_mode compliance) int *result; int res; + /* #warning debug code ahead */ + /* return 1; */ + result = get_compliance_cache (compliance, 1); if (result && *result != -1) diff --git a/common/compliance.h b/common/compliance.h index ead11472c..111fdc74b 100644 --- a/common/compliance.h +++ b/common/compliance.h @@ -50,6 +50,7 @@ enum pk_use_case /* Flags to distinguish public key algorithm variants. */ #define PK_ALGO_FLAG_RSAPSS 1 /* Use rsaPSS padding. */ +#define PK_ALGO_FLAG_ECC18 256 /* GCRY_PK_ECC is used in a generic way. */ int gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, diff --git a/common/dotlock.c b/common/dotlock.c index 74186b776..fe6d7fe71 100644 --- a/common/dotlock.c +++ b/common/dotlock.c @@ -291,6 +291,7 @@ # include # include # include +# include #endif #include #include @@ -393,9 +394,18 @@ struct dotlock_handle unsigned int locked:1; /* Lock status. */ unsigned int disable:1; /* If true, locking is disabled. */ unsigned int use_o_excl:1; /* Use open (O_EXCL) for locking. */ + unsigned int by_parent:1; /* Parent does the locking. */ + unsigned int no_write:1; /* No write to the lockfile. */ int extra_fd; /* A place for the caller to store an FD. */ + /* An optional info callback - see dotlock_set_info_cb. */ + int (*info_cb)(dotlock_t, void *, + enum dotlock_reasons reason, + const char *,...); + void *info_cb_value; + + #ifdef HAVE_DOSISH_SYSTEM HANDLE lockhd; /* The W32 handle of the lock file. */ #else /*!HAVE_DOSISH_SYSTEM */ @@ -545,8 +555,15 @@ read_lockfile (dotlock_t h, int *same_node, int *r_fd) if ( (fd = open (h->lockname, O_RDONLY)) == -1 ) { int e = errno; - my_info_2 ("error opening lockfile '%s': %s\n", - h->lockname, strerror(errno) ); + if (errno != ENOENT) + { + my_info_2 ("error opening lockfile '%s': %s\n", + h->lockname, strerror(errno) ); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR, + "error opening lockfile '%s': %s\n", + h->lockname, strerror (errno) ); + } if (buffer != buffer_space) xfree (buffer); my_set_errno (e); /* Need to return ERRNO here. */ @@ -564,6 +581,10 @@ read_lockfile (dotlock_t h, int *same_node, int *r_fd) { int e = errno; my_info_1 ("error reading lockfile '%s'\n", h->lockname ); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR, + "error reading lockfile '%s': %s\n", + h->lockname, strerror (errno) ); close (fd); if (buffer != buffer_space) xfree (buffer); @@ -583,6 +604,9 @@ read_lockfile (dotlock_t h, int *same_node, int *r_fd) if (nread < 11) { my_info_1 ("invalid size of lockfile '%s'\n", h->lockname); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_INV_FILE, + "invalid size of lockfile '%s'\n", h->lockname); if (buffer != buffer_space) xfree (buffer); my_set_errno (EINVAL); @@ -594,6 +618,9 @@ read_lockfile (dotlock_t h, int *same_node, int *r_fd) || !pid ) { my_error_2 ("invalid pid %d in lockfile '%s'\n", pid, h->lockname); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_INV_FILE, + "invalid pid %d in lockfile '%s'\n", pid, h->lockname); if (buffer != buffer_space) xfree (buffer); my_set_errno (EINVAL); @@ -655,6 +682,80 @@ use_hardlinks_p (const char *tname) #ifdef HAVE_POSIX_SYSTEM +static int +dotlock_get_process_id (dotlock_t h) +{ + return h->by_parent? (int)getppid(): (int)getpid(); +} + +static int +dotlock_detect_tname (dotlock_t h) +{ + struct stat sb; + DIR *dir; + char *dirname; + char *basename; + struct dirent *d; + int r; + + if (stat (h->lockname, &sb)) + return -1; + + basename = make_basename (h->lockname, NULL); + dirname = make_dirname (h->lockname); + + dir = opendir (dirname); + if (dir == NULL) + { + xfree (basename); + xfree (dirname); + return -1; + } + + while ((d = readdir (dir))) + if (sb.st_ino == d->d_ino && strcmp (d->d_name, basename)) + break; + + if (d) + { + int len = strlen (h->tname); + int dlen = strlen (d->d_name); + const char *tname_path; + + if (dlen > len) + { + xfree (basename); + xfree (dirname); + return -1; + } + + strcpy (stpcpy (stpcpy (h->tname, dirname), DIRSEP_S), d->d_name); + h->use_o_excl = 0; + tname_path = strchr (h->tname + strlen (dirname) + 2, '.'); + if (!tname_path) + { + xfree (basename); + xfree (dirname); + return -1; + } + h->nodename_off = tname_path - h->tname + 1; + } + else + h->use_o_excl = 1; + + r = closedir (dir); + if (r) + { + xfree (basename); + xfree (dirname); + return r; + } + + xfree (basename); + xfree (dirname); + return 0; +} + /* Locking core for Unix. It used a temporary file and the link system call to make locking an atomic operation. */ static dotlock_t @@ -667,8 +768,10 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock) int dirpartlen; struct utsname utsbuf; size_t tnamelen; + int pid; - snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid() ); + pid = dotlock_get_process_id (h); + snprintf (pidstr, sizeof pidstr, "%10d\n", pid); /* Create a temporary file. */ if ( uname ( &utsbuf ) ) @@ -702,10 +805,17 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock) } h->nodename_len = strlen (nodename); + if (h->no_write) + { + memset (h->tname, '_', tnamelen); + h->tname[tnamelen] = 0; + goto skip_write; + } + snprintf (h->tname, tnamelen, "%.*s/.#lk%p.", dirpartlen, dirpart, h ); h->nodename_off = strlen (h->tname); snprintf (h->tname+h->nodename_off, tnamelen - h->nodename_off, - "%s.%d", nodename, (int)getpid ()); + "%s.%d", nodename, pid); do { @@ -722,6 +832,10 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock) UNLOCK_all_lockfiles (); my_error_2 (_("failed to create temporary file '%s': %s\n"), h->tname, strerror (errno)); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_WAITING, + _("failed to create temporary file '%s': %s\n"), + h->tname, strerror (errno)); xfree (h->tname); xfree (h); my_set_errno (saveerrno); @@ -755,11 +869,16 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock) int saveerrno = errno; my_error_2 ("can't check whether hardlinks are supported for '%s': %s\n" , h->tname, strerror (saveerrno)); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_CONFIG_TEST, + "can't check whether hardlinks are supported for '%s': %s\n" + , h->tname, strerror (saveerrno)); my_set_errno (saveerrno); } goto write_failed; } + skip_write: h->lockname = xtrymalloc (strlen (file_to_lock) + 6 ); if (!h->lockname) { @@ -775,6 +894,20 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock) strcpy (stpcpy (h->lockname, file_to_lock), EXTSEP_S "lock"); UNLOCK_all_lockfiles (); + if (h->no_write) + { + if (dotlock_detect_tname (h) < 0) + { + xfree (h->lockname); + xfree (h->tname); + xfree (h); + my_set_errno (EACCES); + return NULL; + } + + h->locked = 1; + } + return h; write_failed: @@ -783,6 +916,11 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock) all_lockfiles = h->next; UNLOCK_all_lockfiles (); my_error_2 (_("error writing to '%s': %s\n"), h->tname, strerror (errno)); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR, + _("error writing to '%s': %s\n"), + h->tname, strerror (errno)); + if ( fd != -1 ) close (fd); unlink (h->tname); @@ -849,6 +987,10 @@ dotlock_create_w32 (dotlock_t h, const char *file_to_lock) all_lockfiles = h->next; UNLOCK_all_lockfiles (); my_error_2 (_("can't create '%s': %s\n"), h->lockname, w32_strerror (-1)); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR, + _("can't create '%s': %s\n"), + h->lockname, w32_strerror (-1)); xfree (h->lockname); xfree (h); my_set_errno (saveerrno); @@ -873,7 +1015,15 @@ dotlock_create_w32 (dotlock_t h, const char *file_to_lock) POSIX systems a temporary file ".#lk..pid[.threadid] is used. - FLAGS must be 0. + FLAGS may include DOTLOCK_PREPARE_CREATE bit, which only allocates + the handle and requires a further call to dotlock_finish_create. + This can be used to set a callback between these calls. + + FLAGS may include DOTLOCK_LOCK_BY_PARENT bit, when it's the parent + process controlling the lock. This is used by dotlock util. + + FLAGS may include DOTLOCK_LOCKED bit, when it should not create the + lockfile, but to unlock. This is used by dotlock util. The function returns an new handle which needs to be released using destroy_dotlock but gets also released at the termination of the @@ -885,8 +1035,13 @@ dotlock_create (const char *file_to_lock, unsigned int flags) { static int initialized; dotlock_t h; +#ifndef HAVE_DOSISH_SYSTEM + int by_parent = 0; + int no_write = 0; +#endif - if ( !initialized ) + if ( !(flags & DOTLOCK_LOCK_BY_PARENT) + && !initialized ) { atexit (dotlock_remove_lockfiles); initialized = 1; @@ -895,7 +1050,15 @@ dotlock_create (const char *file_to_lock, unsigned int flags) if ( !file_to_lock ) return NULL; /* Only initialization was requested. */ - if (flags) +#ifndef HAVE_DOSISH_SYSTEM + if ((flags & DOTLOCK_LOCK_BY_PARENT) || (flags & DOTLOCK_LOCKED)) + { + by_parent = !!(flags & DOTLOCK_LOCK_BY_PARENT); + no_write = !!(flags & DOTLOCK_LOCKED); + flags &= ~(DOTLOCK_LOCK_BY_PARENT | DOTLOCK_LOCKED); + } +#endif + if ((flags & ~DOTLOCK_PREPARE_CREATE)) { my_set_errno (EINVAL); return NULL; @@ -905,6 +1068,10 @@ dotlock_create (const char *file_to_lock, unsigned int flags) if (!h) return NULL; h->extra_fd = -1; +#ifndef HAVE_DOSISH_SYSTEM + h->by_parent = by_parent; + h->no_write = no_write; +#endif if (never_lock) { @@ -916,6 +1083,24 @@ dotlock_create (const char *file_to_lock, unsigned int flags) return h; } + if ((flags & DOTLOCK_PREPARE_CREATE)) + return h; + else + return dotlock_finish_create (h, file_to_lock); +} + + +/* This function may be used along with dotlock_create (file_name, + * DOTLOCK_PREPARE_CREATE) to finish the creation call. The given + * filename shall be the same as passed to dotlock_create. On success + * the same handle H is returned, on error NULL is returned and H is + * released. */ +dotlock_t +dotlock_finish_create (dotlock_t h, const char *file_to_lock) +{ + if (!h || !file_to_lock) + return NULL; + #ifdef HAVE_DOSISH_SYSTEM return dotlock_create_w32 (h, file_to_lock); #else /*!HAVE_DOSISH_SYSTEM */ @@ -942,6 +1127,24 @@ dotlock_get_fd (dotlock_t h) } +/* Set a callback function for info diagnostics. The callback + * function CB is called with the handle, the opaque value OPAQUE, a + * reason code, and a format string with its arguments. The callback + * shall return 0 to continue operation or true in which case the + * current function will be terminated with an error. */ +void +dotlock_set_info_cb (dotlock_t h, + int (*cb)(dotlock_t, void *, + enum dotlock_reasons reason, + const char *,...), + void *opaque) +{ + h->info_cb = cb; + h->info_cb_value = opaque; +} + + + #ifdef HAVE_POSIX_SYSTEM /* Unix specific code of destroy_dotlock. */ @@ -952,7 +1155,6 @@ dotlock_destroy_unix (dotlock_t h) unlink (h->lockname); if (h->tname && !h->use_o_excl) unlink (h->tname); - xfree (h->tname); } #endif /*HAVE_POSIX_SYSTEM*/ @@ -998,15 +1200,28 @@ dotlock_destroy (dotlock_t h) UNLOCK_all_lockfiles (); /* Then destroy the lock. */ - if (!h->disable) + if (!h->disable + && (!h->by_parent || h->no_write)) { + /* NOTE: under the condition of (by_parent && !no_write), + it doesn't come here. So, the lock file remains. */ #ifdef HAVE_DOSISH_SYSTEM dotlock_destroy_w32 (h); #else /* !HAVE_DOSISH_SYSTEM */ dotlock_destroy_unix (h); #endif /* HAVE_DOSISH_SYSTEM */ - xfree (h->lockname); } + +#ifdef HAVE_POSIX_SYSTEM + /* When DOTLOCK_LOCK_BY_PARENT and lock fails, + the temporary file created should be removed. */ + if (h->by_parent && !h->no_write && !h->locked) + if (h->tname && !h->use_o_excl) + unlink (h->tname); + + xfree (h->tname); +#endif + xfree (h->lockname); xfree(h); } @@ -1061,6 +1276,7 @@ static int dotlock_take_unix (dotlock_t h, long timeout) { int wtime = 0; + int timedout = 0; int sumtime = 0; int pid; int lastpid = -1; @@ -1089,6 +1305,10 @@ dotlock_take_unix (dotlock_t h, long timeout) saveerrno = errno; my_error_2 ("lock not made: open(O_EXCL) of '%s' failed: %s\n", h->lockname, strerror (saveerrno)); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR, + "lock not made: open(O_EXCL) of '%s' failed: %s\n", + h->lockname, strerror (saveerrno)); my_set_errno (saveerrno); return -1; } @@ -1096,7 +1316,8 @@ dotlock_take_unix (dotlock_t h, long timeout) { char pidstr[16]; - snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid()); + snprintf (pidstr, sizeof pidstr, "%10d\n", + dotlock_get_process_id (h)); if (write (fd, pidstr, 11 ) == 11 && write (fd, h->tname + h->nodename_off,h->nodename_len) == h->nodename_len @@ -1110,6 +1331,10 @@ dotlock_take_unix (dotlock_t h, long timeout) saveerrno = errno; my_error_2 ("lock not made: writing to '%s' failed: %s\n", h->lockname, strerror (errno)); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR, + "lock not made: writing to '%s' failed: %s\n", + h->lockname, strerror (errno)); close (fd); unlink (h->lockname); my_set_errno (saveerrno); @@ -1128,6 +1353,10 @@ dotlock_take_unix (dotlock_t h, long timeout) saveerrno = errno; my_error_1 ("lock not made: Oops: stat of tmp file failed: %s\n", strerror (errno)); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR, + "lock not made: Oops: stat of tmp file failed: %s\n", + strerror (errno)); /* In theory this might be a severe error: It is possible that link succeeded but stat failed due to changed permissions. We can't do anything about it, though. */ @@ -1149,16 +1378,19 @@ dotlock_take_unix (dotlock_t h, long timeout) { saveerrno = errno; my_info_0 ("cannot read lockfile\n"); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR, + "cannot read lockfile\n"); my_set_errno (saveerrno); return -1; } my_info_0 ("lockfile disappeared\n"); goto again; } - else if ( (pid == getpid() && same_node) + else if ( (pid == dotlock_get_process_id (h) && same_node && !h->by_parent) || (same_node && kill (pid, 0) && errno == ESRCH) ) - /* Stale lockfile is detected. */ { + /* Stale lockfile is detected. */ struct stat sb; /* Check if it's unlocked during examining the lockfile. */ @@ -1201,6 +1433,9 @@ dotlock_take_unix (dotlock_t h, long timeout) unlink (h->lockname); my_info_1 (_("removing stale lockfile (created by %d)\n"), pid); close (fd); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_STALE_REMOVED, + _("removing stale lockfile (created by %d)\n"), pid); goto again; } @@ -1218,6 +1453,8 @@ dotlock_take_unix (dotlock_t h, long timeout) wtime = 0; /* Reset because owner chnaged. */ wtimereal = next_wait_interval (&wtime, &timeout); + if (!timeout) + timedout = 1; /* remember. */ sumtime += wtimereal; if (sumtime >= 1500) @@ -1225,6 +1462,15 @@ dotlock_take_unix (dotlock_t h, long timeout) sumtime = 0; my_info_3 (_("waiting for lock (held by %d%s) %s...\n"), pid, maybe_dead, maybe_deadlock(h)? _("(deadlock?) "):""); + if (h->info_cb + && h->info_cb (h, h->info_cb_value, DOTLOCK_WAITING, + _("waiting for lock (held by %d%s) %s...\n"), + pid, maybe_dead, + maybe_deadlock(h)? _("(deadlock?) "):"")) + { + my_set_errno (ECANCELED); + return -1; + } } tv.tv_sec = wtimereal / 1000; @@ -1233,7 +1479,7 @@ dotlock_take_unix (dotlock_t h, long timeout) goto again; } - my_set_errno (EACCES); + my_set_errno (timedout? ETIMEDOUT : EACCES); return -1; } #endif /*HAVE_POSIX_SYSTEM*/ @@ -1246,6 +1492,7 @@ static int dotlock_take_w32 (dotlock_t h, long timeout) { int wtime = 0; + int timedout = 0; int w32err; OVERLAPPED ovl; @@ -1264,7 +1511,11 @@ dotlock_take_w32 (dotlock_t h, long timeout) { my_error_2 (_("lock '%s' not made: %s\n"), h->lockname, w32_strerror (w32err)); - my_set_errno (map_w32_to_errno (w32err)); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR, + _("lock '%s' not made: %s\n"), + h->lockname, w32_strerror (w32err)); + _set_errno (map_w32_to_errno (w32err)); return -1; } @@ -1273,15 +1524,26 @@ dotlock_take_w32 (dotlock_t h, long timeout) int wtimereal; wtimereal = next_wait_interval (&wtime, &timeout); + if (!timeout) + timedout = 1; /* remember. */ if (wtime >= 800) - my_info_1 (_("waiting for lock %s...\n"), h->lockname); + { + my_info_1 (_("waiting for lock %s...\n"), h->lockname); + if (h->info_cb + && h->info_cb (h, h->info_cb_value, DOTLOCK_WAITING, + _("waiting for lock %s...\n"), h->lockname)) + { + my_set_errno (ECANCELED); + return -1; + } + } Sleep (wtimereal); goto again; } - my_set_errno (EACCES); + my_set_errno (timedout? ETIMEDOUT : EACCES); return -1; } #endif /*HAVE_DOSISH_SYSTEM*/ @@ -1328,12 +1590,18 @@ dotlock_release_unix (dotlock_t h) { saveerrno = errno; my_error_0 ("release_dotlock: lockfile error\n"); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR, + "release_dotlock: lockfile error\n"); my_set_errno (saveerrno); return -1; } - if ( pid != getpid() || !same_node ) + if ( pid != dotlock_get_process_id (h) || !same_node ) { my_error_1 ("release_dotlock: not our lock (pid=%d)\n", pid); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_CONFLICT, + "release_dotlock: not our lock (pid=%d)\n", pid); my_set_errno (EACCES); return -1; } @@ -1343,6 +1611,10 @@ dotlock_release_unix (dotlock_t h) saveerrno = errno; my_error_1 ("release_dotlock: error removing lockfile '%s'\n", h->lockname); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR, + "release_dotlock: error removing lockfile '%s'\n", + h->lockname); my_set_errno (saveerrno); return -1; } @@ -1363,10 +1635,15 @@ dotlock_release_w32 (dotlock_t h) memset (&ovl, 0, sizeof ovl); if (!UnlockFileEx (h->lockhd, 0, 1, 0, &ovl)) { - int saveerrno = map_w32_to_errno (GetLastError ()); + int ec = (int)GetLastError (); + my_error_2 ("release_dotlock: error removing lockfile '%s': %s\n", - h->lockname, w32_strerror (-1)); - my_set_errno (saveerrno); + h->lockname, w32_strerror (ec)); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_FILE_ERROR, + "release_dotlock: error removing lockfile '%s': %s\n", + h->lockname, w32_strerror (ec)); + my_set_errno (map_w32_to_errno (ec)); return -1; } @@ -1397,6 +1674,9 @@ dotlock_release (dotlock_t h) if ( !h->locked ) { my_debug_1 ("Oops, '%s' is not locked\n", h->lockname); + if (h->info_cb) + h->info_cb (h, h->info_cb_value, DOTLOCK_NOT_LOCKED, + "Oops, '%s' is not locked\n", h->lockname); return 0; } diff --git a/common/dotlock.h b/common/dotlock.h index 03131bb0c..0f52d4037 100644 --- a/common/dotlock.h +++ b/common/dotlock.h @@ -97,12 +97,35 @@ extern "C" struct dotlock_handle; typedef struct dotlock_handle *dotlock_t; +enum dotlock_reasons + { + DOTLOCK_CONFIG_TEST, /* Can't check system - function terminates. */ + DOTLOCK_FILE_ERROR, /* General file error - function terminates. */ + DOTLOCK_INV_FILE, /* Invalid file - function terminates. */ + DOTLOCK_CONFLICT, /* Something is wrong - function terminates. */ + DOTLOCK_NOT_LOCKED, /* Not locked - No action required. */ + DOTLOCK_STALE_REMOVED, /* Stale lock file was removed - retrying. */ + DOTLOCK_WAITING /* Waiting for the lock - may be terminated. */ + }; + +/* Flags for dotlock_create. */ +#define DOTLOCK_PREPARE_CREATE (1U << 5) /* Require dotlock_finish_create. */ +#define DOTLOCK_LOCK_BY_PARENT (1U << 6) /* Used by dotlock util. */ +#define DOTLOCK_LOCKED (1U << 7) /* Used by dotlock util. */ + void dotlock_disable (void); dotlock_t dotlock_create (const char *file_to_lock, unsigned int flags); +dotlock_t dotlock_finish_create (dotlock_t h, const char *file_to_lock); void dotlock_set_fd (dotlock_t h, int fd); int dotlock_get_fd (dotlock_t h); +void dotlock_set_info_cb (dotlock_t h, + int (*cb)(dotlock_t, void *, + enum dotlock_reasons reason, + const char *,...), + void *opaque); void dotlock_destroy (dotlock_t h); int dotlock_take (dotlock_t h, long timeout); +int dotlock_is_locked (dotlock_t h); int dotlock_release (dotlock_t h); void dotlock_remove_lockfiles (void); diff --git a/common/homedir.c b/common/homedir.c index 286685feb..2b99c9bdc 100644 --- a/common/homedir.c +++ b/common/homedir.c @@ -77,6 +77,14 @@ #endif +/* Mode flags for unix_rootdir. */ +enum wantdir_values { + WANTDIR_ROOT = 0, + WANTDIR_SYSCONF, + WANTDIR_SOCKET +}; + + /* The GnuPG homedir. This is only accessed by the functions * gnupg_homedir and gnupg_set_homedir. Malloced. */ static char *the_gnupg_homedir; @@ -491,11 +499,12 @@ w32_rootdir (void) * file system. If WANT_SYSCONFDIR is true the optional sysconfdir * entry is returned. */ static const char * -unix_rootdir (int want_sysconfdir) +unix_rootdir (enum wantdir_values wantdir) { static int checked; static char *dir; /* for the rootdir */ static char *sdir; /* for the sysconfdir */ + static char *s2dir; /* for the socketdir */ if (!checked) { @@ -510,8 +519,10 @@ unix_rootdir (int want_sysconfdir) estream_t fp; char *rootdir; char *sysconfdir; + char *socketdir; const char *name; int ignoreall = 0; + int okay; for (;;) { @@ -602,45 +613,44 @@ unix_rootdir (int want_sysconfdir) linelen = 0; rootdir = NULL; sysconfdir = NULL; + socketdir = NULL; while ((length = es_read_line (fp, &line, &linelen, NULL)) > 0) { + static const char *names[] = + { + "rootdir", + "sysconfdir", + "socketdir", + ".enable" + }; + int i; + size_t n; + /* Strip NL and CR, if present. */ while (length > 0 && (line[length - 1] == '\n' || line[length - 1] == '\r')) line[--length] = 0; trim_spaces (line); - if (!strncmp (line, "rootdir=", 8)) + /* Find the stamement. */ + name = NULL; + for (i=0; i < DIM (names); i++) { - name = "rootdir"; - p = line + 8; + n = strlen (names[i]); + if (!strncmp (line, names[i], n)) + { + while (line[n] == ' ' || line[n] == '\t') + n++; + if (line[n] == '=') + { + name = names[i]; + p = line + n + 1; + break; + } + } } - else if (!strncmp (line, "rootdir =", 9)) /* (What a kludge) */ - { - name = "rootdir"; - p = line + 9; - } - else if (!strncmp (line, "sysconfdir=", 11)) - { - name = "sysconfdir"; - p = line + 11; - } - else if (!strncmp (line, "sysconfdir =", 12)) /* (What a kludge) */ - { - name = "sysconfdir"; - p = line + 12; - } - else if (!strncmp (line, ".enable=", 8)) - { - name = ".enable"; - p = line + 8; - } - else if (!strncmp (line, ".enable =", 9)) - { - name = ".enable"; - p = line + 9; - } - else - continue; + if (!name) + continue; /* Statement not known. */ + trim_spaces (p); p = substitute_envvars (p); if (!p) @@ -665,6 +675,11 @@ unix_rootdir (int want_sysconfdir) xfree (sysconfdir); sysconfdir = p; } + else if (!strcmp (name, "socketdir")) + { + xfree (socketdir); + socketdir = p; + } else { xfree (rootdir); @@ -680,6 +695,7 @@ unix_rootdir (int want_sysconfdir) xfree (line); xfree (rootdir); xfree (sysconfdir); + xfree (socketdir); checked = 1; return NULL; } @@ -687,29 +703,26 @@ unix_rootdir (int want_sysconfdir) xfree (buffer); xfree (line); + okay = 0; if (ignoreall) - { - xfree (rootdir); - xfree (sysconfdir); - sdir = dir = NULL; - } + ; else if (!rootdir || !*rootdir || *rootdir != '/') { log_info ("invalid rootdir '%s' specified in gpgconf.ctl\n", rootdir); - xfree (rootdir); - xfree (sysconfdir); - dir = NULL; } else if (sysconfdir && (!*sysconfdir || *sysconfdir != '/')) { log_info ("invalid sysconfdir '%s' specified in gpgconf.ctl\n", sysconfdir); - xfree (rootdir); - xfree (sysconfdir); - dir = NULL; + } + else if (socketdir && (!*socketdir || *socketdir != '/')) + { + log_info ("invalid socketdir '%s' specified in gpgconf.ctl\n", + socketdir); } else { + okay = 1; while (*rootdir && rootdir[strlen (rootdir)-1] == '/') rootdir[strlen (rootdir)-1] = 0; dir = rootdir; @@ -723,11 +736,34 @@ unix_rootdir (int want_sysconfdir) gpgrt_annotate_leaked_object (sdir); /* log_info ("want sysconfdir '%s'\n", sdir); */ } + if (socketdir) + { + while (*socketdir && socketdir[strlen (socketdir)-1] == '/') + socketdir[strlen (socketdir)-1] = 0; + s2dir = socketdir; + gpgrt_annotate_leaked_object (s2dir); + /* log_info ("want socketdir '%s'\n", s2dir); */ + } + } + + if (!okay) + { + xfree (rootdir); + xfree (sysconfdir); + xfree (socketdir); + dir = sdir = s2dir = NULL; } checked = 1; } - return want_sysconfdir? sdir : dir; + switch (wantdir) + { + case WANTDIR_ROOT: return dir; + case WANTDIR_SYSCONF: return sdir; + case WANTDIR_SOCKET: return s2dir; + } + + return NULL; /* Not reached. */ } #endif /* Unix */ @@ -1038,7 +1074,8 @@ _gnupg_socketdir_internal (int skip_checks, unsigned *r_info) }; int i; struct stat sb; - char prefix[19 + 1 + 20 + 6 + 1]; + char prefixbuffer[19 + 1 + 20 + 6 + 1]; + const char *prefix; const char *s; char *name = NULL; @@ -1053,35 +1090,42 @@ _gnupg_socketdir_internal (int skip_checks, unsigned *r_info) * as a background process with no (desktop) user logged in. Thus * we better don't do that. */ - /* Check whether we have a /run/[gnupg/]user dir. */ - for (i=0; bases[i]; i++) + prefix = unix_rootdir (WANTDIR_SOCKET); + if (!prefix) { - snprintf (prefix, sizeof prefix, "%s/user/%u", - bases[i], (unsigned int)getuid ()); - if (!stat (prefix, &sb) && S_ISDIR(sb.st_mode)) - break; - } - if (!bases[i]) - { - *r_info |= 2; /* No /run/user directory. */ - goto leave; + /* gpgconf.ctl does not specify a directory. Check whether we + * have the usual /run/[gnupg/]user dir. */ + for (i=0; bases[i]; i++) + { + snprintf (prefixbuffer, sizeof prefixbuffer, "%s/user/%u", + bases[i], (unsigned int)getuid ()); + prefix = prefixbuffer; + if (!stat (prefix, &sb) && S_ISDIR(sb.st_mode)) + break; + } + if (!bases[i]) + { + *r_info |= 2; /* No /run/user directory. */ + goto leave; + } + + if (sb.st_uid != getuid ()) + { + *r_info |= 4; /* Not owned by the user. */ + if (!skip_checks) + goto leave; + } + + if (strlen (prefix) + 7 >= sizeof prefixbuffer) + { + *r_info |= 1; /* Ooops: Buffer too short to append "/gnupg". */ + goto leave; + } + strcat (prefixbuffer, "/gnupg"); } - if (sb.st_uid != getuid ()) - { - *r_info |= 4; /* Not owned by the user. */ - if (!skip_checks) - goto leave; - } - - if (strlen (prefix) + 7 >= sizeof prefix) - { - *r_info |= 1; /* Ooops: Buffer too short to append "/gnupg". */ - goto leave; - } - strcat (prefix, "/gnupg"); - - /* Check whether the gnupg sub directory has proper permissions. */ + /* Check whether the gnupg sub directory (or the specified diretory) + * has proper permissions. */ if (stat (prefix, &sb)) { if (errno != ENOENT) @@ -1241,7 +1285,7 @@ gnupg_sysconfdir (void) } return name; #else /*!HAVE_W32_SYSTEM*/ - const char *dir = unix_rootdir (1); + const char *dir = unix_rootdir (WANTDIR_SYSCONF); if (dir) return dir; else @@ -1270,7 +1314,7 @@ gnupg_bindir (void) else return rdir; #else /*!HAVE_W32_SYSTEM*/ - rdir = unix_rootdir (0); + rdir = unix_rootdir (WANTDIR_ROOT); if (rdir) { if (!name) @@ -1297,7 +1341,7 @@ gnupg_libexecdir (void) static char *name; const char *rdir; - rdir = unix_rootdir (0); + rdir = unix_rootdir (WANTDIR_ROOT); if (rdir) { if (!name) @@ -1327,7 +1371,7 @@ gnupg_libdir (void) #else /*!HAVE_W32_SYSTEM*/ const char *rdir; - rdir = unix_rootdir (0); + rdir = unix_rootdir (WANTDIR_ROOT); if (rdir) { if (!name) @@ -1358,7 +1402,7 @@ gnupg_datadir (void) #else /*!HAVE_W32_SYSTEM*/ const char *rdir; - rdir = unix_rootdir (0); + rdir = unix_rootdir (WANTDIR_ROOT); if (rdir) { if (!name) @@ -1390,7 +1434,7 @@ gnupg_localedir (void) #else /*!HAVE_W32_SYSTEM*/ const char *rdir; - rdir = unix_rootdir (0); + rdir = unix_rootdir (WANTDIR_ROOT); if (rdir) { if (!name) diff --git a/common/name-value.c b/common/name-value.c index 0dffc63b4..ea6a84f56 100644 --- a/common/name-value.c +++ b/common/name-value.c @@ -48,6 +48,7 @@ struct name_value_container struct name_value_entry *first; struct name_value_entry *last; unsigned int private_key_mode:1; + unsigned int modified:1; }; @@ -87,11 +88,15 @@ my_error (gpg_err_code_t ec) /* Allocation and deallocation. */ -/* Allocate a private key container structure. */ +/* Allocate a name value container structure. */ nvc_t nvc_new (void) { - return xtrycalloc (1, sizeof (struct name_value_container)); + nvc_t nvc; + nvc = xtrycalloc (1, sizeof (struct name_value_container)); + if (nvc) + nvc->modified = 1; + return nvc; } @@ -142,6 +147,24 @@ nvc_release (nvc_t pk) xfree (pk); } + +/* Return the modified-flag of the container and clear it if CLEAR is + * set. That flag is set for a new container and set with each + * update. */ +int +nvc_modified (nvc_t pk, int clear) +{ + int modified; + + if (!pk) + return 0; + modified = pk->modified; + if (clear) + pk->modified = 0; + return modified; +} + + /* Dealing with names and values. */ @@ -427,6 +450,8 @@ _nvc_add (nvc_t pk, char *name, char *value, strlist_t raw_value, else pk->first = pk->last = e; + pk->modified = 1; + leave: if (err) { @@ -476,36 +501,29 @@ nvc_set (nvc_t pk, const char *name, const char *value) e = nvc_lookup (pk, name); if (e) - { - char *v; - - v = xtrystrdup (value); - if (v == NULL) - return my_error_from_syserror (); - - free_strlist_wipe (e->raw_value); - e->raw_value = NULL; - if (e->value) - wipememory (e->value, strlen (e->value)); - xfree (e->value); - e->value = v; - - return 0; - } + return nve_set (pk, e, value); else return nvc_add (pk, name, value); } -/* Update entry E to VALUE. */ +/* Update entry E to VALUE. PK is optional; if given its modified + * flag will be updated. */ gpg_error_t -nve_set (nve_t e, const char *value) +nve_set (nvc_t pk, nve_t e, const char *value) { char *v; if (!e) return GPG_ERR_INV_ARG; + if (e->value && value && !strcmp (e->value, value)) + { + /* Setting same value - ignore this call and don't set the + * modified flag (if PK is given). */ + return 0; + } + v = xtrystrdup (value? value:""); if (!v) return my_error_from_syserror (); @@ -516,6 +534,8 @@ nve_set (nve_t e, const char *value) wipememory (e->value, strlen (e->value)); xfree (e->value); e->value = v; + if (pk) + pk->modified = 1; return 0; } @@ -536,6 +556,7 @@ nvc_delete (nvc_t pk, nve_t entry) pk->last = entry->prev; nve_release (entry, pk->private_key_mode); + pk->modified = 1; } diff --git a/common/name-value.h b/common/name-value.h index b3fc2f63c..dfded6678 100644 --- a/common/name-value.h +++ b/common/name-value.h @@ -50,6 +50,9 @@ nvc_t nvc_new_private_key (void); /* Release a name value container structure. */ void nvc_release (nvc_t pk); +/* Return the modified flag and optionally clear it. */ +int nvc_modified (nvc_t pk, int clear); + /* Get the name. */ char *nve_name (nve_t pke); @@ -92,8 +95,8 @@ gpg_error_t nvc_add (nvc_t pk, const char *name, const char *value); first entry is updated. */ gpg_error_t nvc_set (nvc_t pk, const char *name, const char *value); -/* Update entry E to VALUE. */ -gpg_error_t nve_set (nve_t e, const char *value); +/* Update entry E to VALUE. PK is optional. */ +gpg_error_t nve_set (nvc_t pk, nve_t e, const char *value); /* Delete the given entry from PK. */ void nvc_delete (nvc_t pk, nve_t pke); diff --git a/common/openpgpdefs.h b/common/openpgpdefs.h index c97995568..d8672ac47 100644 --- a/common/openpgpdefs.h +++ b/common/openpgpdefs.h @@ -122,6 +122,9 @@ typedef enum SIGSUBPKT_ATTST_SIGS = 37, /* Attested Certifications. */ SIGSUBPKT_KEY_BLOCK = 38, /* Entire key used. */ + SIGSUBPKT_META_HASH = 40, /* Literal Data Meta Hash. */ + SIGSUBPKT_TRUST_ALIAS = 41, /* Trust Alias. */ + SIGSUBPKT_FLAG_CRITICAL = 128 } sigsubpkttype_t; diff --git a/common/t-dotlock.c b/common/t-dotlock.c index 994ef1be3..87e62bb33 100644 --- a/common/t-dotlock.c +++ b/common/t-dotlock.c @@ -52,6 +52,7 @@ #ifdef HAVE_W32_SYSTEM #define DIM(v) (sizeof(v)/sizeof((v)[0])) + const char * w32_strerror (int ec) { @@ -174,6 +175,11 @@ strconcat (const char *s1, ...) #define PGM "t-dotlock" +static int opt_silent; + + + + #ifndef HAVE_W32_SYSTEM static volatile int ctrl_c_pending_flag; static void @@ -217,6 +223,9 @@ inf (const char *format, ...) { va_list arg_ptr; + if (opt_silent) + return; + va_start (arg_ptr, format); fprintf (stderr, PGM "[%lu]: ", (unsigned long)getpid ()); vfprintf (stderr, format, arg_ptr); @@ -225,15 +234,35 @@ inf (const char *format, ...) } +static int +lock_info_cb (dotlock_t h, void *opaque, enum dotlock_reasons reason, + const char *format, ...) +{ + va_list arg_ptr; + + va_start (arg_ptr, format); + fprintf (stderr, PGM "[%lu]: info_cb: reason %d, ", + (unsigned long)getpid (), (int)reason); + vfprintf (stderr, format, arg_ptr); + va_end (arg_ptr); + return 0; +} + + static void lock_and_unlock (const char *fname) { dotlock_t h; unsigned long usec; - h = dotlock_create (fname, 0); + h = dotlock_create (fname, DOTLOCK_PREPARE_CREATE); if (!h) die ("error creating lock file for '%s': %s", fname, strerror (errno)); + dotlock_set_info_cb (h, lock_info_cb, NULL); + h = dotlock_finish_create (h, fname); + if (!h) + die ("error finishing lock file creation for '%s': %s", + fname, strerror (errno)); inf ("lock created"); do @@ -270,6 +299,11 @@ main (int argc, char **argv) ctrl_c_pending_flag = 1; argc--; } + if (argc > 1 && !strcmp (argv[1], "--silent")) + { + opt_silent = 1; + argc--; + } if (argc > 1) fname = argv[argc-1]; diff --git a/doc/opt-homedir.texi b/doc/opt-homedir.texi index 07993d29d..15ae1970d 100644 --- a/doc/opt-homedir.texi +++ b/doc/opt-homedir.texi @@ -13,13 +13,3 @@ directory stated through the environment variable @env{GNUPGHOME} or On Windows systems it is possible to install GnuPG as a portable application. In this case only this command line option is considered, all other ways to set a home directory are ignored. - -@efindex gpgconf.ctl -To install GnuPG as a portable application under Windows, create an -empty file named @file{gpgconf.ctl} in the same directory as the tool -@file{gpgconf.exe}. The root of the installation is then that -directory; or, if @file{gpgconf.exe} has been installed directly below -a directory named @file{bin}, its parent directory. You also need to -make sure that the following directories exist and are writable: -@file{ROOT/home} for the GnuPG home and @file{ROOT@value{LOCALCACHEDIR}} -for internal cache files. diff --git a/doc/tools.texi b/doc/tools.texi index a7c141cfc..07a50820b 100644 --- a/doc/tools.texi +++ b/doc/tools.texi @@ -1122,10 +1122,36 @@ More fields may be added in future to the output. @table @file +@item gpgconf.ctl +@cindex gpgconf.ctl + Under Unix @file{gpgconf.ctl} may be used to change some of the + compiled in directories where the GnuPG components are expected. This + file is expected in the same directory as @file{gpgconf}. The + physical installation directories are evaluated and no symlinks. + Blank lines and lines starting with pound sign are ignored in the + file. The keywords must be followed by optional white space, an equal + sign, optional white space, and the value. Environment variables are + substituted in standard shell manner, the final value must start with + a slash, trailing slashes are stripped. Valid keywords are + @code{rootdir}, @code{sysconfdir}, @code{socketdir}, and + @code{.enable}. No errors are printed for unknown keywords. The + @code{.enable} keyword is special: if the keyword is used and its + value evaluates to true the entire file is ignored. + + Under Windows this file is used to install GnuPG as a portable + application. An empty file named @file{gpgconf.ctl} is expected in + the same directory as the tool @file{gpgconf.exe}. The root of the + installation is then that directory; or, if @file{gpgconf.exe} has + been installed directly below a directory named @file{bin}, its parent + directory. You also need to make sure that the following directories + exist and are writable: @file{ROOT/home} for the GnuPG home and + @file{ROOT@value{LOCALCACHEDIR}} for internal cache files. + + @item /etc/gnupg/gpgconf.conf @cindex gpgconf.conf If this file exists, it is processed as a global configuration file. - This is a legacy mechanism which should not be used tigether with + This is a legacy mechanism which should not be used together with the modern global per component configuration files. A commented example can be found in the @file{examples} directory of the distribution. diff --git a/doc/wks.texi b/doc/wks.texi index 1959151a1..8c5fc557c 100644 --- a/doc/wks.texi +++ b/doc/wks.texi @@ -243,6 +243,21 @@ Display a brief help page and exit. @end table +@noindent +@mansect examples +@chapheading Examples + +To use the services with clients lacking integrated support, the +mailcap mechanism can be used. Simply put: +@example +application/vnd.gnupg.wks; \ + @value{BINDIR}/gpg-wks-client -v --read --send; \ + needsterminal; \ + description=WKS message +@end example +into the @file{/etc/mailcap}. This assumes that a /usr/lib/sendmail +is installed. With this configuration any real mail programs will run +gpg-wks-client for messages received from a Web Key Service. @mansect see also @ifset isman diff --git a/g10/call-agent.c b/g10/call-agent.c index c44c1cddb..f545b6690 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -1996,7 +1996,7 @@ agent_get_passphrase (const char *cache_id, char *arg4 = NULL; membuf_t data; struct default_inq_parm_s dfltparm; - int have_newsymkey; + int have_newsymkey, wasconf; memset (&dfltparm, 0, sizeof dfltparm); @@ -2048,10 +2048,14 @@ agent_get_passphrase (const char *cache_id, xfree (arg4); init_membuf_secure (&data, 64); + wasconf = assuan_get_flag (agent_ctx, ASSUAN_CONFIDENTIAL); + assuan_begin_confidential (agent_ctx); rc = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); + if (!wasconf) + assuan_end_confidential (agent_ctx); if (rc) xfree (get_membuf (&data, NULL)); diff --git a/g10/call-keyboxd.c b/g10/call-keyboxd.c index 960979aae..bc3806f0b 100644 --- a/g10/call-keyboxd.c +++ b/g10/call-keyboxd.c @@ -393,6 +393,23 @@ keydb_get_keyblock (KEYDB_HANDLE hd, kbnode_t *ret_kb) } +/* Default status callback used to show diagnostics from the keyboxd */ +static gpg_error_t +keydb_default_status_cb (void *opaque, const char *line) +{ + const char *s; + + (void)opaque; + + if ((s = has_leading_keyword (line, "NOTE"))) + log_info (_("Note: %s\n"), s); + else if ((s = has_leading_keyword (line, "WARNING"))) + log_info (_("WARNING: %s\n"), s); + + return 0; +} + + /* Communication object for STORE commands. */ struct store_parm_s @@ -472,7 +489,8 @@ keydb_update_keyblock (ctrl_t ctrl, KEYDB_HANDLE hd, kbnode_t kb) err = assuan_transact (hd->kbl->ctx, "STORE --update", NULL, NULL, store_inq_cb, &parm, - NULL, NULL); + keydb_default_status_cb, hd); + leave: iobuf_close (iobuf); @@ -523,7 +541,7 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb) err = assuan_transact (hd->kbl->ctx, "STORE --insert", NULL, NULL, store_inq_cb, &parm, - NULL, NULL); + keydb_default_status_cb, hd); leave: iobuf_close (iobuf); @@ -569,7 +587,7 @@ keydb_delete_keyblock (KEYDB_HANDLE hd) err = assuan_transact (hd->kbl->ctx, line, NULL, NULL, NULL, NULL, - NULL, NULL); + keydb_default_status_cb, hd); leave: return err; @@ -656,6 +674,8 @@ search_status_cb (void *opaque, const char *line) } } } + else + err = keydb_default_status_cb (opaque, line); return err; } diff --git a/g10/keyedit.c b/g10/keyedit.c index 21c1ee8d8..1f614fb7e 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -4794,7 +4794,7 @@ fail: /* * Ask for a new additional decryption subkey and add it to the key * block. Returns true if the keyblock was changed and false - * otherwise. If ADSKFPR is not NULL, this fucntion has been called + * otherwise. If ADSKFPR is not NULL, this function has been called * by quick_addadsk and gives the fingerprint of the to be added key. */ static int diff --git a/kbx/backend-sqlite.c b/kbx/backend-sqlite.c index 202897e91..ec891da6d 100644 --- a/kbx/backend-sqlite.c +++ b/kbx/backend-sqlite.c @@ -521,11 +521,45 @@ run_sql_statement (const char *sqlstr) } +static int +dblock_info_cb (dotlock_t h, void *opaque, enum dotlock_reasons reason, + const char *format, ...) +{ + ctrl_t ctrl = opaque; + va_list arg_ptr; + gpg_error_t err; + int rc = 0; + char tmpbuf[200]; + + (void)h; + + if (reason == DOTLOCK_WAITING) + { + if (format) + { + va_start (arg_ptr, format); + gpgrt_vsnprintf (tmpbuf, sizeof tmpbuf, format, arg_ptr); + va_end (arg_ptr); + } + else + *tmpbuf = 0; + err = kbxd_status_printf (ctrl, "NOTE", "database_open %u %s", + gpg_error (GPG_ERR_LOCKED), tmpbuf); + if (err) + { + log_error ("sending status line failed: %s\n", gpg_strerror (err)); + rc = 1; /* snprintf failed. */ + } + + } + return rc; +} + /* Create and initialize a new SQL database file if it does not * exists; else open it and check that all required objects are * available. */ static gpg_error_t -create_or_open_database (const char *filename) +create_or_open_database (ctrl_t ctrl, const char *filename) { gpg_error_t err; int res; @@ -542,7 +576,16 @@ create_or_open_database (const char *filename) /* To avoid races with other temporary instances of keyboxd trying * to create or update the database, we run the database with a lock * file held. */ - database_lock = dotlock_create (filename, 0); + database_lock = dotlock_create (filename, DOTLOCK_PREPARE_CREATE); + if (!database_lock) + { + err = gpg_error_from_syserror (); + if (opt.verbose) + log_info ("can't allocate dotlock handle: %s\n", gpg_strerror (err)); + goto leave; + } + dotlock_set_info_cb (database_lock, dblock_info_cb, ctrl); + database_lock = dotlock_finish_create (database_lock, filename); if (!database_lock) { err = gpg_error_from_syserror (); @@ -556,7 +599,7 @@ create_or_open_database (const char *filename) goto leave; } - if (dotlock_take (database_lock, -1)) + if (dotlock_take (database_lock, 10000)) { err = gpg_error_from_syserror (); /* This is something bad. Probably a stale lockfile. */ @@ -646,7 +689,8 @@ create_or_open_database (const char *filename) { log_error (_("error creating database '%s': %s\n"), filename, gpg_strerror (err)); - dotlock_release (database_lock); + if (dotlock_is_locked (database_lock)) + dotlock_release (database_lock); dotlock_destroy (database_lock); database_lock = NULL; } @@ -660,7 +704,6 @@ gpg_error_t be_sqlite_add_resource (ctrl_t ctrl, backend_handle_t *r_hd, const char *filename, int readonly) { - gpg_error_t err; backend_handle_t hd; (void)ctrl; @@ -672,19 +715,10 @@ be_sqlite_add_resource (ctrl_t ctrl, backend_handle_t *r_hd, return gpg_error_from_syserror (); hd->db_type = DB_TYPE_SQLITE; strcpy (hd->filename, filename); - - err = create_or_open_database (filename); - if (err) - goto leave; - hd->backend_id = be_new_backend_id (); *r_hd = hd; - hd = NULL; - - leave: - xfree (hd); - return err; + return 0; } @@ -1139,6 +1173,10 @@ be_sqlite_search (ctrl_t ctrl, log_assert (backend_hd && backend_hd->db_type == DB_TYPE_SQLITE); log_assert (request); + err = create_or_open_database (ctrl, backend_hd->filename); + if (err) + return err; + acquire_mutex (); /* Find the specific request part or allocate it. */ diff --git a/kbx/kbx-client-util.c b/kbx/kbx-client-util.c index 2ea3f5ef7..b900586c8 100644 --- a/kbx/kbx-client-util.c +++ b/kbx/kbx-client-util.c @@ -405,10 +405,10 @@ kbx_client_data_cmd (kbx_client_data_t kcd, const char *command, status_cb, status_cb_value); if (err) { - if (gpg_err_code (err) != GPG_ERR_NOT_FOUND - && gpg_err_code (err) != GPG_ERR_NOTHING_FOUND) - log_debug ("%s: finished command with error: %s\n", - __func__, gpg_strerror (err)); + /* if (gpg_err_code (err) != GPG_ERR_NOT_FOUND */ + /* && gpg_err_code (err) != GPG_ERR_NOTHING_FOUND) */ + /* log_debug ("%s: finished command with error: %s\n", */ + /* __func__, gpg_strerror (err)); */ xfree (get_membuf (&mb, &len)); kcd->dlineerr = err; goto leave; diff --git a/kbx/keyboxd.c b/kbx/keyboxd.c index e8e41486c..f9792789d 100644 --- a/kbx/keyboxd.c +++ b/kbx/keyboxd.c @@ -1257,6 +1257,7 @@ create_directories (void) { if (!opt.quiet) log_info (_("directory '%s' created\n"), home); + create_public_keys_directory (home); } } } diff --git a/po/ja.po b/po/ja.po index 73d141545..d067bf202 100644 --- a/po/ja.po +++ b/po/ja.po @@ -9,9 +9,9 @@ # msgid "" msgstr "" -"Project-Id-Version: gnupg 2.4.1\n" +"Project-Id-Version: gnupg 2.4.3\n" "Report-Msgid-Bugs-To: translations@gnupg.org\n" -"PO-Revision-Date: 2023-05-25 11:12+0900\n" +"PO-Revision-Date: 2023-11-20 10:50+0900\n" "Last-Translator: NIIBE Yutaka \n" "Language-Team: none\n" "Language: ja\n" @@ -1788,14 +1788,14 @@ msgstr "S2Kモードのため、SKESKパケットを使えません\n" msgid "using cipher %s.%s\n" msgstr "暗号方式 %s.%s を使います\n" -#, c-format -msgid "'%s' already compressed\n" -msgstr "'%s'はもう圧縮済みです\n" - #, c-format msgid "WARNING: '%s' is an empty file\n" msgstr "*警告*: '%s'は空のファイルです\n" +#, c-format +msgid "'%s' already compressed\n" +msgstr "'%s'はもう圧縮済みです\n" + #, c-format msgid "digest algorithm '%s' may not be used in %s mode\n" msgstr "ダイジェスト・アルゴリズム'%s'を%sモードで使うことはできません\n" @@ -1869,7 +1869,7 @@ msgstr "'%s'への書き込み\n" #, c-format msgid "key %s: key material on-card - skipped\n" -msgstr "鍵%s: 鍵マテリアルはカード上にあります - スキップします\n" +msgstr "鍵%s: 鍵の実体はカード上にあります - スキップします\n" #, c-format msgid "exporting secret keys not allowed\n" @@ -2893,7 +2893,7 @@ msgstr "鍵 %s: エージェントへの送信エラー: %s\n" #, c-format msgid "key %s: card reference is overridden by key material\n" -msgstr "鍵 %s: カード参照が鍵マテリアルで上書きされます\n" +msgstr "鍵 %s: カード参照が鍵の実体で上書きされます\n" #. TRANSLATORS: For a smartcard, each private key on host has a #. * reference (stub) to a smartcard and actual private key data diff --git a/scd/apdu.c b/scd/apdu.c index deb1134e6..98158648b 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -775,8 +775,8 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, if (DBG_CARD_IO) { /* Do not dump the PIN in a VERIFY command. */ - if (apdulen > 5 && apdu[1] == 0x20) - log_debug ("PCSC_data: %02X %02X %02X %02X %02X [redacted]\n", + if (apdulen > 5 && apdu[1] == 0x20 && !opt.debug_allow_pin_logging) + log_debug ("PCSC_data: %02X %02X %02X %02X %02X [hidden]\n", apdu[0], apdu[1], apdu[2], apdu[3], apdu[4]); else log_printhex (apdu, apdulen, "PCSC_data:"); @@ -1564,8 +1564,8 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen, if (DBG_CARD_IO) { /* Do not dump the PIN in a VERIFY command. */ - if (apdulen > 5 && apdu[1] == 0x20) - log_debug (" raw apdu: %02x%02x%02x%02x%02x [redacted]\n", + if (apdulen > 5 && apdu[1] == 0x20 && !opt.debug_allow_pin_logging) + log_debug (" raw apdu: %02x%02x%02x%02x%02x [hidden]\n", apdu[0], apdu[1], apdu[2], apdu[3], apdu[4]); else log_printhex (apdu, apdulen, " raw apdu:"); @@ -3049,7 +3049,9 @@ send_le (int slot, int class, int ins, int p0, int p1, sw, (unsigned int)resultlen); if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA)) { - if (all_zero_p (result, resultlen)) + if (!resultlen) + ; + else if (all_zero_p (result, resultlen)) log_debug (" dump: [all zero]\n"); else log_printhex (result, resultlen, " dump:"); diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 014cd9395..3bc709602 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -3499,6 +3499,31 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, log_error (_("error getting new PIN: %s\n"), gpg_strerror (rc)); goto leave; } + + if (set_resetcode) + { + size_t bufferlen = strlen (pinvalue); + + if (bufferlen != 0 && bufferlen < 8) + { + log_error (_("Reset Code is too short; minimum length is %d\n"), 8); + rc = gpg_error (GPG_ERR_BAD_RESET_CODE); + goto leave; + } + } + else + { + if (chvno == 3) + minlen = 8; + + if (strlen (pinvalue) < minlen) + { + log_info (_("PIN for CHV%d is too short;" + " minimum length is %d\n"), chvno, minlen); + rc = gpg_error (GPG_ERR_BAD_PIN); + goto leave; + } + } } @@ -3533,24 +3558,15 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, } else if (set_resetcode) { - size_t bufferlen = strlen (pinvalue); + size_t bufferlen; + char *buffer = NULL; - if (bufferlen != 0 && bufferlen < 8) - { - log_error (_("Reset Code is too short; minimum length is %d\n"), 8); - rc = gpg_error (GPG_ERR_BAD_RESET_CODE); - } - else - { - char *buffer = NULL; + rc = pin2hash_if_kdf (app, 0, pinvalue, &buffer, &bufferlen); + if (!rc) + rc = iso7816_put_data (app_get_slot (app), + 0, 0xD3, buffer, bufferlen); - rc = pin2hash_if_kdf (app, 0, pinvalue, &buffer, &bufferlen); - if (!rc) - rc = iso7816_put_data (app_get_slot (app), - 0, 0xD3, buffer, bufferlen); - - wipe_and_free (buffer, bufferlen); - } + wipe_and_free (buffer, bufferlen); } else if (reset_mode) { @@ -4733,9 +4749,11 @@ ecc_writekey (app_t app, ctrl_t ctrl, if (algo == PUBKEY_ALGO_ECDH && !ecdh_param) { - log_error ("opgp: ecdh parameters missing\n"); - err = gpg_error (GPG_ERR_INV_VALUE); - goto leave; + /* In case this is used by older clients we fallback to our + * default ecc parameters. */ + log_info ("opgp: using default ecdh parameters\n"); + ecdh_param = ecdh_params (curve); + ecdh_param_len = 4; } oidstr = openpgp_curve_to_oid (curve, &n, NULL); diff --git a/scd/app-p15.c b/scd/app-p15.c index 4338a623e..8edd737a6 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -74,8 +74,10 @@ typedef enum CARD_TYPE_MICARDO, CARD_TYPE_CARDOS_50, CARD_TYPE_CARDOS_53, + CARD_TYPE_CARDOS_54, CARD_TYPE_AET, /* A.E.T. Europe JCOP card. */ - CARD_TYPE_BELPIC /* Belgian eID card specs. */ + CARD_TYPE_BELPIC, /* Belgian eID card specs. */ + CARD_TYPE_STARCOS_32 } card_type_t; @@ -86,7 +88,8 @@ typedef enum { CARD_PRODUCT_UNKNOWN, CARD_PRODUCT_RSCS, /* Rohde&Schwarz Cybersecurity */ - CARD_PRODUCT_DTRUST, /* D-Trust GmbH (bundesdruckerei.de) */ + CARD_PRODUCT_DTRUST3, /* D-Trust GmbH (bundesdruckerei.de) */ + CARD_PRODUCT_DTRUST4, CARD_PRODUCT_GENUA, /* GeNUA mbH */ CARD_PRODUCT_NEXUS /* Technology Nexus */ } @@ -123,17 +126,23 @@ static struct CARD_TYPE_CARDOS_50 }, /* CardOS 5.0 */ { 11, X("\x3b\xd2\x18\x00\x81\x31\xfe\x58\xc9\x03\x16"), CARD_TYPE_CARDOS_53 }, /* CardOS 5.3 */ + { 11, X("\x3b\xd2\x18\x00\x81\x31\xfe\x58\xc9\x04\x11"), + CARD_TYPE_CARDOS_54 }, /* CardOS 5.4 */ { 24, X("\x3b\xfe\x18\x00\x00\x80\x31\xfe\x45\x53\x43\x45" "\x36\x30\x2d\x43\x44\x30\x38\x31\x2d\x6e\x46\xa9"), CARD_TYPE_AET }, + { 25, X("\x3b\x9f\x96\x81\xb1\xfe\x45\x1f\x07\x00\x64\x05" + "\x1e\xb2\x00\x31\xb0\x73\x96\x21\xdb\x05\x90\x00\x5c"), + CARD_TYPE_STARCOS_32 }, { 0 } }; #undef X -/* Macro to test for CardOS 5.0 and 5.3. */ +/* Macro to test for CardOS 5.0, 5.3 and 5.4. */ #define IS_CARDOS_5(a) ((a)->app_local->card_type == CARD_TYPE_CARDOS_50 \ - || (a)->app_local->card_type == CARD_TYPE_CARDOS_53) + || (a)->app_local->card_type == CARD_TYPE_CARDOS_53 \ + || (a)->app_local->card_type == CARD_TYPE_CARDOS_54) /* The default PKCS-15 home DF */ #define DEFAULT_HOME_DF 0x5015 @@ -147,6 +156,11 @@ static char const pkcs15_aid[] = { 0xA0, 0, 0, 0, 0x63, static char const pkcs15be_aid[] = { 0xA0, 0, 0, 0x01, 0x77, 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 }; +/* The D-TRUST Card 4.x variant - dito */ +static char const pkcs15dtrust4_aid[] = { 0xE8, 0x28, 0xBD, 0x08, 0x0F, 0xA0, + 0x00, 0x00, 0x01, 0x67, 0x45, 0x53, + 0x49, 0x47, 0x4E }; + /* The PIN types as defined in pkcs#15 v1.1 */ typedef enum @@ -518,6 +532,8 @@ struct app_local_s /*** Local prototypes. ***/ static gpg_error_t select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen); +static gpg_error_t select_df_by_path (app_t app, const unsigned short *path, + size_t pathlen); static gpg_error_t keygrip_from_prkdf (app_t app, prkdf_object_t prkdf); static gpg_error_t readcert_by_cdf (app_t app, cdf_object_t cdf, unsigned char **r_cert, size_t *r_certlen); @@ -536,8 +552,10 @@ cardtype2str (card_type_t cardtype) case CARD_TYPE_MICARDO: return "Micardo"; case CARD_TYPE_CARDOS_50: return "CardOS 5.0"; case CARD_TYPE_CARDOS_53: return "CardOS 5.3"; + case CARD_TYPE_CARDOS_54: return "CardOS 5.4"; case CARD_TYPE_BELPIC: return "Belgian eID"; case CARD_TYPE_AET: return "AET"; + case CARD_TYPE_STARCOS_32:return "STARCOS 3.2"; } return ""; } @@ -549,7 +567,8 @@ cardproduct2str (card_product_t cardproduct) { case CARD_PRODUCT_UNKNOWN: return ""; case CARD_PRODUCT_RSCS: return "R&S"; - case CARD_PRODUCT_DTRUST: return "D-Trust"; + case CARD_PRODUCT_DTRUST3: return "D-Trust 3"; + case CARD_PRODUCT_DTRUST4: return "D-Trust 4.1/4.4"; case CARD_PRODUCT_GENUA: return "GeNUA"; case CARD_PRODUCT_NEXUS: return "Nexus"; } @@ -765,20 +784,28 @@ select_and_read_record (app_t app, unsigned short efid, int recno, /* This function calls select file to read a file using a complete - path which may or may not start at the master file (MF). */ + * path which may or may not start at the master file (MF). If + * EXPECT_DF is set a directory or file is expected - otherwise an + * elementary file expected. */ static gpg_error_t -select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen) +select_by_path (app_t app, const unsigned short *path, size_t pathlen, + int expect_df) { gpg_error_t err; int i, j; + int home_df_used = 0; if (!pathlen) return gpg_error (GPG_ERR_INV_VALUE); - /* log_debug ("%s: path=", __func__); */ - /* for (j=0; j < pathlen; j++) */ - /* log_printf ("%s%04hX", j? "/":"", path[j]); */ - /* log_printf ("%s\n",app->app_local->direct_path_selection?" (direct)":"");*/ + if (opt.debug) + { + log_debug ("%s: path=", __func__); + for (j=0; j < pathlen; j++) + log_printf ("%s%04hX", j? "/":"", path[j]); + log_printf ("%s\n",expect_df?" (DF requested)":""); + log_printf ("%s\n",app->app_local->direct_path_selection?" (direct)":""); + } if (app->app_local->direct_path_selection) { @@ -791,35 +818,17 @@ select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen) 0); } else - err = iso7816_select_path (app_get_slot (app), path, pathlen, - app->app_local->home_df); + { + home_df_used = 1; + err = iso7816_select_path (app_get_slot (app), path, pathlen, + app->app_local->home_df); + } if (err) { log_error ("p15: error selecting path "); goto err_print_path; } } - else if (pathlen > 1 && path[0] == 0x3fff) - { - err = iso7816_select_file (app_get_slot (app), 0x3f00, 0); - if (err) - { - log_error ("p15: error selecting part %d from path ", 0); - goto err_print_path; - } - path++; - pathlen--; - for (i=0; i < pathlen; i++) - { - err = iso7816_select_file (app_get_slot (app), - path[i], (i+1 == pathlen)? 2 : 1); - if (err) - { - log_error ("p15: error selecting part %d from path ", i); - goto err_print_path; - } - } - } else { if (pathlen && *path != 0x3f00 ) @@ -829,7 +838,7 @@ select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen) for (i=0; i < pathlen; i++) { err = iso7816_select_file (app_get_slot (app), - path[i], !(i+1 == pathlen)); + path[i], (expect_df || (i+1 < pathlen))); if (err) { log_error ("p15: error selecting part %d from path ", i); @@ -842,7 +851,7 @@ select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen) err_print_path: if (pathlen && *path != 0x3f00 ) log_printf ("3F00/"); - else + else if (home_df_used) log_printf ("%04hX/", app->app_local->home_df); for (j=0; j < pathlen; j++) log_printf ("%s%04hX", j? "/":"", path[j]); @@ -851,6 +860,20 @@ select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen) } +static gpg_error_t +select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen) +{ + return select_by_path (app, path, pathlen, 0); +} + + +static gpg_error_t +select_df_by_path (app_t app, const unsigned short *path, size_t pathlen) +{ + return select_by_path (app, path, pathlen, 1); +} + + /* Parse a cert Id string (or a key Id string) and return the binary object Id string in a newly allocated buffer stored at R_OBJID and R_OBJIDLEN. On Error NULL will be stored there and an error code @@ -3245,7 +3268,7 @@ read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result) if (aodf->max_length_valid) log_printf (" max=%lu", aodf->max_length); if (aodf->pad_char_valid) - log_printf (" pad=0x%02x", aodf->pad_char); + log_printf (" pad=0x%02x", (unsigned char)aodf->pad_char); log_info ("p15: flags="); s = ""; @@ -3495,7 +3518,7 @@ read_ef_tokeninfo (app_t app) ul |= (*p++) & 0xff; n--; } - if (ul) + if (ul > 1) { log_error ("p15: invalid version %lu in TokenInfo\n", ul); err = gpg_error (GPG_ERR_INV_OBJ); @@ -3829,7 +3852,14 @@ read_p15_info (app_t app) && !strncmp (app->app_local->token_label, "D-TRUST Card V3", 15) && app->app_local->card_type == CARD_TYPE_CARDOS_50) { - app->app_local->card_product = CARD_PRODUCT_DTRUST; + app->app_local->card_product = CARD_PRODUCT_DTRUST3; + } + if (!app->app_local->card_product + && app->app_local->token_label + && !strncmp (app->app_local->token_label, "D-TRUST Card 4.", 15) + && app->app_local->card_type == CARD_TYPE_CARDOS_54) + { + app->app_local->card_product = CARD_PRODUCT_DTRUST4; } @@ -5007,7 +5037,7 @@ prepare_verify_pin (app_t app, const char *keyref, } - if (app->app_local->card_product == CARD_PRODUCT_DTRUST) + if (app->app_local->card_product == CARD_PRODUCT_DTRUST3) { /* According to our protocol analysis we need to select a * special AID here. Before that the master file needs to be @@ -5023,6 +5053,13 @@ prepare_verify_pin (app_t app, const char *keyref, log_error ("p15: error selecting D-TRUST's AID for key %s: %s\n", keyref, gpg_strerror (err)); } + else if (prkdf && app->app_local->card_type == CARD_TYPE_STARCOS_32) + { + err = select_df_by_path (app, prkdf->path, prkdf->pathlen); + if (err) + log_error ("p15: error selecting file for key %s: %s\n", + keyref, gpg_strerror (err)); + } else if (prkdf) { /* Standard case: Select the key file. Note that this may @@ -5258,7 +5295,8 @@ verify_pin (app_t app, if (prkdf && prkdf->usageflags.non_repudiation && (app->app_local->card_type == CARD_TYPE_BELPIC - || app->app_local->card_product == CARD_PRODUCT_DTRUST)) + || app->app_local->card_product == CARD_PRODUCT_DTRUST3 + || app->app_local->card_product == CARD_PRODUCT_DTRUST4)) label = _("||Please enter the PIN for the key to create " "qualified signatures."); else if (aodf->pinflags.so_pin) @@ -5622,7 +5660,8 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, goto leave; } if (app->app_local->card_type == CARD_TYPE_BELPIC - || app->app_local->card_product == CARD_PRODUCT_NEXUS) + || app->app_local->card_product == CARD_PRODUCT_NEXUS + || app->app_local->card_product == CARD_PRODUCT_DTRUST4) { /* The default for these cards is to use a plain hash. We * assume that due to the used certificate the correct hash @@ -5708,6 +5747,31 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, else err = micardo_mse (app, prkdf->path[prkdf->pathlen-1]); } + else if (app->app_local->card_product == CARD_PRODUCT_DTRUST4) + { + if (prkdf->is_ecc) + { + /* Not implemented due to lacking test hardware. */ + log_info ("Note: ECC is not yet implemented for DTRUST 4 cards\n"); + err = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + } + else + { + /* The D-TRUST Card 4.x doesn't support setting a security + * environment, at least as specified in the specs. Insted a + * predefined security environment has to be loaded depending on the + * cipher and message digest used. The spec states SE-ID 0x25 for + * SHA256, 0x26 for SHA384 and 0x27 for SHA512, when using PKCS#1 + * padding. But this matters only if the message digest is computed + * on the card. When providing the digest info and a pre-calculated + * hash, all security environments yield the same result. Thus we + * choose 0x25. + * + * Note: For PSS signatures, different values apply. */ + err = iso7816_manage_security_env (app_get_slot (app), + 0xf3, 0x25, NULL, 0); + } + } else if (prkdf->key_reference_valid) { unsigned char mse[3]; @@ -5863,7 +5927,7 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, /* The next is guess work for CardOS. */ - if (app->app_local->card_product == CARD_PRODUCT_DTRUST) + if (app->app_local->card_product == CARD_PRODUCT_DTRUST3) { /* From analyzing an USB trace of a Windows signing application * we see that the SE is simply reset to 0x14. It seems to be @@ -5880,6 +5944,22 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, 0xF3, 0x14, NULL, 0); } + else if (app->app_local->card_product == CARD_PRODUCT_DTRUST4) + { + if (prkdf->is_ecc) + { + /* Not implemented due to lacking test hardware. */ + log_info ("Note: ECC is not yet implemented for DTRUST 4 cards\n"); + err = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + } + else + { + /* SE-ID 0x31 is for PKCS#1 padded cryptograms. For OAEP encryption + * schemes, different values apply. */ + err = iso7816_manage_security_env (app_get_slot (app), + 0xF3, 0x31, NULL, 0); + } + } else if (prkdf->key_reference_valid) { unsigned char mse[9]; @@ -5923,7 +6003,8 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, le_value = prkdf->keynbits / 8; } - if (app->app_local->card_product == CARD_PRODUCT_DTRUST) + if (app->app_local->card_product == CARD_PRODUCT_DTRUST3 + || app->app_local->card_product == CARD_PRODUCT_DTRUST4) padind = 0x81; if (prkdf->is_ecc && IS_CARDOS_5(app)) @@ -6185,6 +6266,13 @@ app_select_p15 (app_t app) rc = iso7816_select_application_ext (slot, pkcs15_aid, sizeof pkcs15_aid, 1, &fci, &fcilen); + if (rc) + { + /* D-TRUST Card 4.x uses a different AID. */ + rc = iso7816_select_application_ext (slot, pkcs15dtrust4_aid, + sizeof pkcs15dtrust4_aid, 1, + &fci, &fcilen); + } if (rc) { /* Not found: Try to locate it from 2F00. We use direct path selection here because it seems that the Belgian eID card @@ -6284,9 +6372,11 @@ app_select_p15 (app_t app) { case CARD_TYPE_CARDOS_50: case CARD_TYPE_CARDOS_53: + case CARD_TYPE_CARDOS_54: direct = 1; break; case CARD_TYPE_AET: + case CARD_TYPE_STARCOS_32: app->app_local->no_extended_mode = 1; break; default: diff --git a/scd/iso7816.c b/scd/iso7816.c index 47e16056c..703f1fdab 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -166,7 +166,7 @@ iso7816_select_mf (int slot) { int sw; - sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE, 0x000, 0x0c, -1, NULL); + sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE, 0x00, 0x0c, -1, NULL); return map_sw (sw); } diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 0376cbfba..e49b2ce42 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -79,6 +79,7 @@ enum cmd_and_opt_values oDebugAllowCoreDump, oDebugCCIDDriver, oDebugLogTid, + oDebugAllowPINLogging, oDebugAssuanLogCats, oNoGreeting, oNoOptions, @@ -138,6 +139,7 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"), ARGPARSE_s_n (oDebugCCIDDriver, "debug-ccid-driver", "@"), ARGPARSE_s_n (oDebugLogTid, "debug-log-tid", "@"), + ARGPARSE_s_n (oDebugAllowPINLogging, "debug-allow-pin-logging", "@"), ARGPARSE_p_u (oDebugAssuanLogCats, "debug-assuan-log-cats", "@"), ARGPARSE_s_s (oLogFile, "log-file", N_("|FILE|write a log to FILE")), @@ -583,6 +585,9 @@ main (int argc, char **argv ) case oDebugLogTid: log_set_pid_suffix_cb (tid_log_callback); break; + case oDebugAllowPINLogging: + opt.debug_allow_pin_logging = 1; + break; case oDebugAssuanLogCats: set_libassuan_log_cats (pargs.r.ret_ulong); break; diff --git a/scd/scdaemon.h b/scd/scdaemon.h index 68136b886..7b82d1b21 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -66,6 +66,7 @@ struct strlist_t disabled_applications; /* Card applications we do not want to use. */ unsigned long card_timeout; /* Disconnect after N seconds of inactivity. */ + int debug_allow_pin_logging; /* Allow PINs in debug output. */ } opt; diff --git a/sm/call-agent.c b/sm/call-agent.c index 7f7205f26..acce19058 100644 --- a/sm/call-agent.c +++ b/sm/call-agent.c @@ -1323,6 +1323,7 @@ gpgsm_agent_ask_passphrase (ctrl_t ctrl, const char *desc_msg, int repeat, char *arg4 = NULL; membuf_t data; struct default_inq_parm_s inq_parm; + int wasconf; *r_passphrase = NULL; @@ -1341,9 +1342,13 @@ gpgsm_agent_ask_passphrase (ctrl_t ctrl, const char *desc_msg, int repeat, xfree (arg4); init_membuf_secure (&data, 64); + wasconf = assuan_get_flag (agent_ctx, ASSUAN_CONFIDENTIAL); + assuan_begin_confidential (agent_ctx); err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &inq_parm, NULL, NULL); + if (!wasconf) + assuan_end_confidential (agent_ctx); if (err) xfree (get_membuf (&data, NULL)); diff --git a/sm/decrypt.c b/sm/decrypt.c index 6121fd278..5a947779f 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -1309,7 +1309,8 @@ gpgsm_decrypt (ctrl_t ctrl, estream_t in_fp, estream_t out_fp) /* Check compliance. */ if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION, - pk_algo, 0, NULL, nbits, curve)) + pk_algo, PK_ALGO_FLAG_ECC18, + NULL, nbits, curve)) { char kidstr[10+1]; diff --git a/sm/encrypt.c b/sm/encrypt.c index 9113028db..16c48c8d5 100644 --- a/sm/encrypt.c +++ b/sm/encrypt.c @@ -577,9 +577,8 @@ int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp, estream_t out_fp) { - int rc = 0; + gpg_error_t err = 0; gnupg_ksba_io_t b64writer = NULL; - gpg_error_t err; ksba_writer_t writer; ksba_reader_t reader = NULL; ksba_cms_t cms = NULL; @@ -607,7 +606,7 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp, log_error(_("no valid recipients given\n")); gpgsm_status (ctrl, STATUS_NO_RECP, "0"); audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, 0); - rc = gpg_error (GPG_ERR_NO_PUBKEY); + err = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } @@ -619,28 +618,26 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp, if (!kh) { log_error (_("failed to allocate keyDB handle\n")); - rc = gpg_error (GPG_ERR_GENERAL); + err = gpg_error (GPG_ERR_GENERAL); goto leave; } err = ksba_reader_new (&reader); + if (!err) + err = ksba_reader_set_cb (reader, encrypt_cb, &encparm); if (err) - rc = err; - if (!rc) - rc = ksba_reader_set_cb (reader, encrypt_cb, &encparm); - if (rc) - goto leave; + goto leave; encparm.fp = data_fp; ctrl->pem_name = "ENCRYPTED MESSAGE"; - rc = gnupg_ksba_create_writer + err = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, out_fp, &writer); - if (rc) + if (err) { - log_error ("can't create writer: %s\n", gpg_strerror (rc)); + log_error ("can't create writer: %s\n", gpg_strerror (err)); goto leave; } @@ -650,17 +647,13 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp, err = ksba_cms_new (&cms); if (err) - { - rc = err; - goto leave; - } + goto leave; err = ksba_cms_set_reader_writer (cms, reader, writer); if (err) { log_error ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } @@ -675,7 +668,6 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp, { log_error ("ksba_cms_set_content_type failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } @@ -687,34 +679,34 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp, log_error (_("cipher algorithm '%s' may not be used in %s mode\n"), opt.def_cipher_algoid, gnupg_compliance_option_string (opt.compliance)); - rc = gpg_error (GPG_ERR_CIPHER_ALGO); + err = gpg_error (GPG_ERR_CIPHER_ALGO); goto leave; } if (!gnupg_rng_is_compliant (opt.compliance)) { - rc = gpg_error (GPG_ERR_FORBIDDEN); + err = gpg_error (GPG_ERR_FORBIDDEN); log_error (_("%s is not compliant with %s mode\n"), "RNG", gnupg_compliance_option_string (opt.compliance)); gpgsm_status_with_error (ctrl, STATUS_ERROR, - "random-compliance", rc); + "random-compliance", err); goto leave; } /* Create a session key */ dek = xtrycalloc_secure (1, sizeof *dek); if (!dek) - rc = out_of_core (); + err = gpg_error_from_syserror (); else - { - dek->algoid = opt.def_cipher_algoid; - rc = init_dek (dek); - } - if (rc) + { + dek->algoid = opt.def_cipher_algoid; + err = init_dek (dek); + } + if (err) { log_error ("failed to create the session key: %s\n", - gpg_strerror (rc)); + gpg_strerror (err)); goto leave; } @@ -723,7 +715,6 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp, { log_error ("ksba_cms_set_content_enc_algo failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } @@ -733,7 +724,7 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp, encparm.buffer = xtrymalloc (encparm.bufsize); if (!encparm.buffer) { - rc = out_of_core (); + err = gpg_error_from_syserror (); goto leave; } @@ -775,12 +766,12 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp, xfree (curve); curve = NULL; - rc = encrypt_dek (dek, cl->cert, pk_algo, &encval); - if (rc) + err = encrypt_dek (dek, cl->cert, pk_algo, &encval); + if (err) { - audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, rc); + audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err); log_error ("encryption failed for recipient no. %d: %s\n", - recpno, gpg_strerror (rc)); + recpno, gpg_strerror (err)); goto leave; } @@ -790,7 +781,6 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp, audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err); log_error ("ksba_cms_add_recipient failed: %s\n", gpg_strerror (err)); - rc = err; xfree (encval); goto leave; } @@ -802,7 +792,6 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp, { log_error ("ksba_cms_set_enc_val failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } } @@ -816,7 +805,7 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp, log_error (_("operation forced to fail due to" " unfulfilled compliance rules\n")); gpgsm_errors_seen = 1; - rc = gpg_error (GPG_ERR_FORBIDDEN); + err = gpg_error (GPG_ERR_FORBIDDEN); goto leave; } @@ -828,7 +817,6 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp, if (err) { log_error ("creating CMS object failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } } @@ -837,15 +825,15 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp, if (encparm.readerror) { log_error ("error reading input: %s\n", strerror (encparm.readerror)); - rc = gpg_error (gpg_err_code_from_errno (encparm.readerror)); + err = gpg_error (gpg_err_code_from_errno (encparm.readerror)); goto leave; } - rc = gnupg_ksba_finish_writer (b64writer); - if (rc) + err = gnupg_ksba_finish_writer (b64writer); + if (err) { - log_error ("write failed: %s\n", gpg_strerror (rc)); + log_error ("write failed: %s\n", gpg_strerror (err)); goto leave; } audit_log (ctrl->audit, AUDIT_ENCRYPTION_DONE); @@ -859,5 +847,5 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp, keydb_release (kh); xfree (dek); xfree (encparm.buffer); - return rc; + return err; } diff --git a/sm/keydb.c b/sm/keydb.c index 512ab1af8..151ae8103 100644 --- a/sm/keydb.c +++ b/sm/keydb.c @@ -1137,6 +1137,23 @@ keydb_set_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int value) } +/* Default status callback used to show diagnostics from the keyboxd */ +static gpg_error_t +keydb_default_status_cb (void *opaque, const char *line) +{ + const char *s; + + (void)opaque; + + if ((s = has_leading_keyword (line, "NOTE"))) + log_info (_("Note: %s\n"), s); + else if ((s = has_leading_keyword (line, "WARNING"))) + log_info (_("WARNING: %s\n"), s); + + return 0; +} + + /* Communication object for Keyboxd STORE commands. */ struct store_parm_s @@ -1200,7 +1217,7 @@ keydb_insert_cert (KEYDB_HANDLE hd, ksba_cert_t cert) err = assuan_transact (hd->kbl->ctx, "STORE --insert", NULL, NULL, store_inq_cb, &parm, - NULL, NULL); + keydb_default_status_cb, hd); goto leave; } @@ -1335,7 +1352,7 @@ keydb_delete (KEYDB_HANDLE hd) err = assuan_transact (hd->kbl->ctx, line, NULL, NULL, NULL, NULL, - NULL, NULL); + keydb_default_status_cb, hd); goto leave; } @@ -1563,6 +1580,8 @@ search_status_cb (void *opaque, const char *line) } } } + else + err = keydb_default_status_cb (opaque, line); return err; } diff --git a/sm/keylist.c b/sm/keylist.c index ed1b74729..47fe69f30 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -532,6 +532,8 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity, { if (gpgsm_cert_has_well_known_private_key (cert)) *truststring = 'w'; /* Well, this is dummy CA. */ + else if (gpg_err_code (valerr) == GPG_ERR_NOT_TRUSTED) + *truststring = 'n'; /* Likely the root cert is not trusted. */ else *truststring = 'i'; } diff --git a/sm/minip12.c b/sm/minip12.c index ae81d821b..1bbe126ae 100644 --- a/sm/minip12.c +++ b/sm/minip12.c @@ -936,6 +936,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (!datalen) { err = gpg_error (GPG_ERR_DECRYPT_FAILED); + ctx->badpass = 1; /* This is the most likley reason. */ goto bailout; } @@ -1461,6 +1462,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (!datalen) { err = gpg_error (GPG_ERR_DECRYPT_FAILED); + ctx->badpass = 1; goto bailout; } diff --git a/sm/sign.c b/sm/sign.c index 8fd111a7d..ec0172b4b 100644 --- a/sm/sign.c +++ b/sm/sign.c @@ -606,8 +606,8 @@ int gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, estream_t data_fp, int detached, estream_t out_fp) { - int i, rc; gpg_error_t err; + int i; gnupg_ksba_io_t b64writer = NULL; ksba_writer_t writer; estream_t sig_fp = NULL; /* Used for detached signatures. */ @@ -630,18 +630,18 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, if (!kh) { log_error (_("failed to allocate keyDB handle\n")); - rc = gpg_error (GPG_ERR_GENERAL); + err = gpg_error (GPG_ERR_GENERAL); goto leave; } if (!gnupg_rng_is_compliant (opt.compliance)) { - rc = gpg_error (GPG_ERR_FORBIDDEN); + err = gpg_error (GPG_ERR_FORBIDDEN); log_error (_("%s is not compliant with %s mode\n"), "RNG", gnupg_compliance_option_string (opt.compliance)); gpgsm_status_with_error (ctrl, STATUS_ERROR, - "random-compliance", rc); + "random-compliance", err); goto leave; } @@ -653,20 +653,20 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, if (binary_detached) { sig_fp = es_fopenmem (0, "w+"); - rc = sig_fp? 0 : gpg_error_from_syserror (); - if (!rc) - rc = gnupg_ksba_create_writer (&b64writer, 0, NULL, sig_fp, &writer); + err = sig_fp? 0 : gpg_error_from_syserror (); + if (!err) + err = gnupg_ksba_create_writer (&b64writer, 0, NULL, sig_fp, &writer); } else { - rc = gnupg_ksba_create_writer + err = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, out_fp, &writer); } - if (rc) + if (err) { - log_error ("can't create writer: %s\n", gpg_strerror (rc)); + log_error ("can't create writer: %s\n", gpg_strerror (err)); goto leave; } @@ -676,17 +676,13 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, err = ksba_cms_new (&cms); if (err) - { - rc = err; - goto leave; - } + goto leave; err = ksba_cms_set_reader_writer (cms, NULL, writer); if (err) { log_debug ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } @@ -703,7 +699,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, { log_debug ("ksba_cms_set_content_type failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } @@ -716,23 +711,23 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, log_error ("no default signer found\n"); gpgsm_status2 (ctrl, STATUS_INV_SGNR, get_inv_recpsgnr_code (GPG_ERR_NO_SECKEY), NULL); - rc = gpg_error (GPG_ERR_GENERAL); + err = gpg_error (GPG_ERR_GENERAL); goto leave; } /* Although we don't check for ambiguous specification we will check that the signer's certificate is usable and valid. */ - rc = gpgsm_cert_use_sign_p (cert, 0); - if (!rc) - rc = gpgsm_validate_chain (ctrl, cert, + err = gpgsm_cert_use_sign_p (cert, 0); + if (!err) + err = gpgsm_validate_chain (ctrl, cert, GNUPG_ISOTIME_NONE, NULL, 0, NULL, 0, NULL); - if (rc) + if (err) { char *tmpfpr; tmpfpr = gpgsm_get_fingerprint_hexstring (cert, 0); gpgsm_status2 (ctrl, STATUS_INV_SGNR, - get_inv_recpsgnr_code (rc), tmpfpr, NULL); + get_inv_recpsgnr_code (err), tmpfpr, NULL); xfree (tmpfpr); goto leave; } @@ -741,7 +736,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, signerlist = xtrycalloc (1, sizeof *signerlist); if (!signerlist) { - rc = out_of_core (); + err = gpg_error_from_syserror (); ksba_cert_release (cert); goto leave; } @@ -822,8 +817,8 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, goto leave; } - if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pk_algo, 0, - NULL, nbits, curve)) + if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pk_algo, + PK_ALGO_FLAG_ECC18, NULL, nbits, curve)) { char kidstr[10+1]; @@ -849,22 +844,21 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, /* Gather certificates of signers and store them in the CMS object. */ for (cl=signerlist; cl; cl = cl->next) { - rc = gpgsm_cert_use_sign_p (cl->cert, 0); - if (rc) + err = gpgsm_cert_use_sign_p (cl->cert, 0); + if (err) goto leave; err = ksba_cms_add_signer (cms, cl->cert); if (err) { log_error ("ksba_cms_add_signer failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } - rc = add_certificate_list (ctrl, cms, cl->cert); - if (rc) + err = add_certificate_list (ctrl, cms, cl->cert); + if (err) { log_error ("failed to store list of certificates: %s\n", - gpg_strerror(rc)); + gpg_strerror (err)); goto leave; } /* Set the hash algorithm we are going to use */ @@ -873,7 +867,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, { log_debug ("ksba_cms_add_digest_algo failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } } @@ -895,7 +888,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, { log_error (_("checking for qualified certificate failed: %s\n"), gpg_strerror (err)); - rc = err; goto leave; } if (*buffer) @@ -903,19 +895,16 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, else err = gpgsm_not_qualified_warning (ctrl, cl->cert); if (err) - { - rc = err; - goto leave; - } + goto leave; } } /* Prepare hashing (actually we are figuring out what we have set above). */ - rc = gcry_md_open (&data_md, 0, 0); - if (rc) + err = gcry_md_open (&data_md, 0, 0); + if (err) { - log_error ("md_open failed: %s\n", gpg_strerror (rc)); + log_error ("md_open failed: %s\n", gpg_strerror (err)); goto leave; } if (DBG_HASHING) @@ -927,7 +916,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, if (!algo) { log_error ("unknown hash algorithm '%s'\n", algoid? algoid:"?"); - rc = gpg_error (GPG_ERR_BUG); + err = gpg_error (GPG_ERR_BUG); goto leave; } gcry_md_enable (data_md, algo); @@ -952,7 +941,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, if ( !digest || !digest_len ) { log_error ("problem getting the hash of the data\n"); - rc = gpg_error (GPG_ERR_BUG); + err = gpg_error (GPG_ERR_BUG); goto leave; } err = ksba_cms_set_message_digest (cms, signer, digest, digest_len); @@ -960,7 +949,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, { log_error ("ksba_cms_set_message_digest failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } } @@ -974,7 +962,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, { log_error ("ksba_cms_set_signing_time failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } } @@ -1016,7 +1003,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, if (err) { log_error ("creating CMS object failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } @@ -1028,8 +1014,8 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, log_assert (!detached); - rc = hash_and_copy_data (data_fp, data_md, writer); - if (rc) + err = hash_and_copy_data (data_fp, data_md, writer); + if (err) goto leave; audit_log (ctrl->audit, AUDIT_GOT_DATA); for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) @@ -1039,7 +1025,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, if ( !digest || !digest_len ) { log_error ("problem getting the hash of the data\n"); - rc = gpg_error (GPG_ERR_BUG); + err = gpg_error (GPG_ERR_BUG); goto leave; } err = ksba_cms_set_message_digest (cms, signer, @@ -1048,7 +1034,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, { log_error ("ksba_cms_set_message_digest failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } } @@ -1058,10 +1043,10 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, /* Compute the signature for all signers. */ gcry_md_hd_t md; - rc = gcry_md_open (&md, 0, 0); - if (rc) + err = gcry_md_open (&md, 0, 0); + if (err) { - log_error ("md_open failed: %s\n", gpg_strerror (rc)); + log_error ("md_open failed: %s\n", gpg_strerror (err)); goto leave; } if (DBG_HASHING) @@ -1086,20 +1071,20 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, } } - rc = ksba_cms_hash_signed_attrs (cms, signer); - if (rc) + err = ksba_cms_hash_signed_attrs (cms, signer); + if (err) { log_debug ("hashing signed attrs failed: %s\n", - gpg_strerror (rc)); + gpg_strerror (err)); gcry_md_close (md); goto leave; } - rc = gpgsm_create_cms_signature (ctrl, cl->cert, - md, cl->hash_algo, &sigval); - if (rc) + err = gpgsm_create_cms_signature (ctrl, cl->cert, + md, cl->hash_algo, &sigval); + if (err) { - audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, rc); + audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, err); gcry_md_close (md); goto leave; } @@ -1111,7 +1096,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, err); log_error ("failed to store the signature: %s\n", gpg_strerror (err)); - rc = err; gcry_md_close (md); goto leave; } @@ -1120,11 +1104,10 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, fpr = gpgsm_get_fingerprint_hexstring (cl->cert, GCRY_MD_SHA1); if (!fpr) { - rc = gpg_error (GPG_ERR_ENOMEM); + err = gpg_error (GPG_ERR_ENOMEM); gcry_md_close (md); goto leave; } - rc = 0; if (opt.verbose) { char *pkalgostr = gpgsm_pubkey_algo_string (cl->cert, NULL); @@ -1141,9 +1124,9 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, signed_at, fpr); if (!buf) - rc = gpg_error_from_syserror (); + err = gpg_error_from_syserror (); xfree (fpr); - if (rc) + if (err) { gcry_md_close (md); goto leave; @@ -1157,10 +1140,10 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, } while (stopreason != KSBA_SR_READY); - rc = gnupg_ksba_finish_writer (b64writer); - if (rc) + err = gnupg_ksba_finish_writer (b64writer); + if (err) { - log_error ("write failed: %s\n", gpg_strerror (rc)); + log_error ("write failed: %s\n", gpg_strerror (err)); goto leave; } @@ -1169,13 +1152,14 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, void *blob = NULL; size_t bloblen; - rc = es_fclose_snatch (sig_fp, &blob, &bloblen); + err = (es_fclose_snatch (sig_fp, &blob, &bloblen)? + gpg_error_from_syserror () : 0); sig_fp = NULL; - if (rc) + if (err) goto leave; - rc = write_detached_signature (ctrl, blob, bloblen, out_fp); + err = write_detached_signature (ctrl, blob, bloblen, out_fp); xfree (blob); - if (rc) + if (err) goto leave; } @@ -1184,9 +1168,9 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, log_info ("signature created\n"); leave: - if (rc) + if (err) log_error ("error creating signature: %s <%s>\n", - gpg_strerror (rc), gpg_strsource (rc) ); + gpg_strerror (err), gpg_strsource (err) ); if (release_signerlist) gpgsm_release_certlist (signerlist); xfree (curve); @@ -1195,5 +1179,5 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, keydb_release (kh); gcry_md_close (data_md); es_fclose (sig_fp); - return rc; + return err; } diff --git a/sm/verify.c b/sm/verify.c index 9c012596d..53d1b468a 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -485,6 +485,7 @@ gpgsm_verify (ctrl_t ctrl, estream_t in_fp, estream_t data_fp, audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo); /* Check compliance. */ + pkalgoflags |= PK_ALGO_FLAG_ECC18; if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION, pkalgo, pkalgoflags, NULL, nbits, pkcurve)) { diff --git a/tools/gpgconf.c b/tools/gpgconf.c index 30dd8edfd..83b4bca21 100644 --- a/tools/gpgconf.c +++ b/tools/gpgconf.c @@ -34,6 +34,7 @@ #include "../common/init.h" #include "../common/status.h" #include "../common/exechelp.h" +#include "../common/dotlock.h" #ifdef HAVE_W32_SYSTEM #include @@ -76,7 +77,9 @@ enum cmd_and_opt_values aCreateSocketDir, aRemoveSocketDir, aApplyProfile, - aShowCodepages + aShowCodepages, + aDotlockLock, + aDotlockUnlock }; @@ -109,7 +112,10 @@ static gpgrt_opt_t opts[] = { aRemoveSocketDir, "remove-socketdir", 256, "@"}, ARGPARSE_c (aShowVersions, "show-versions", ""), ARGPARSE_c (aShowConfigs, "show-configs", ""), + /* hidden commands: for debugging */ ARGPARSE_c (aShowCodepages, "show-codepages", "@"), + ARGPARSE_c (aDotlockLock, "lock", "@"), + ARGPARSE_c (aDotlockUnlock, "unlock", "@"), { 301, NULL, 0, N_("@\nOptions:\n ") }, @@ -604,6 +610,41 @@ query_swdb (estream_t out, const char *name, const char *current_version) } +#if !defined(HAVE_W32_SYSTEM) +/* dotlock tool to handle dotlock by command line + DO_LOCK: 1 for to lock, 0 for unlock + FILENAME: filename for the dotlock */ +static void +dotlock_tool (int do_lock, const char *filename) +{ + dotlock_t h; + unsigned int flags = DOTLOCK_LOCK_BY_PARENT; + + if (!do_lock) + flags |= DOTLOCK_LOCKED; + + h = dotlock_create (filename, flags); + if (!h) + { + if (do_lock) + log_error ("error creating the lock file\n"); + else + log_error ("no lock file found\n"); + return; + } + + if (do_lock) + { + if (dotlock_take (h, 0)) + log_error ("error taking the lock\n"); + } + else + dotlock_release (h); + + dotlock_destroy (h); +} +#endif + /* gpgconf main. */ int main (int argc, char **argv) @@ -669,6 +710,8 @@ main (int argc, char **argv) case aShowVersions: case aShowConfigs: case aShowCodepages: + case aDotlockLock: + case aDotlockUnlock: cmd = pargs.r_opt; break; @@ -1024,7 +1067,32 @@ main (int argc, char **argv) #endif break; + case aDotlockLock: + case aDotlockUnlock: +#if !defined(HAVE_W32_SYSTEM) + if (!fname) + { + es_fprintf (es_stderr, "usage: %s [options] lock|unlock NAME", + GPGCONF_NAME); + es_putc ('\n', es_stderr); + es_fputs (_("Need one NAME argument"), es_stderr); + es_putc ('\n', es_stderr); + gpgconf_failure (GPG_ERR_USER_2); + } + else + { + char *filename; + /* Keybox pubring.db lock is under public-keys.d. */ + if (!strcmp (fname, "pubring.db")) + fname = "public-keys.d/pubring.db"; + + filename = make_absfilename (gnupg_homedir (), fname, NULL); + dotlock_tool (cmd == aDotlockLock, filename); + xfree (filename); + } +#endif + break; } if (outfp != es_stdout)