diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/compliance.c | 13 | ||||
-rw-r--r-- | common/compliance.h | 1 | ||||
-rw-r--r-- | common/dotlock.c | 322 | ||||
-rw-r--r-- | common/dotlock.h | 23 | ||||
-rw-r--r-- | common/homedir.c | 192 | ||||
-rw-r--r-- | common/name-value.c | 61 | ||||
-rw-r--r-- | common/name-value.h | 7 | ||||
-rw-r--r-- | common/openpgpdefs.h | 3 | ||||
-rw-r--r-- | common/t-dotlock.c | 36 |
9 files changed, 538 insertions, 120 deletions
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 <sys/types.h> # include <sys/stat.h> # include <sys/utsname.h> +# include <dirent.h> #endif #include <sys/types.h> #include <sys/time.h> @@ -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.<hostname>.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)) - { - name = "rootdir"; - p = line + 8; - } - else if (!strncmp (line, "rootdir =", 9)) /* (What a kludge) */ - { - name = "rootdir"; - p = line + 9; - } - else if (!strncmp (line, "sysconfdir=", 11)) + /* Find the stamement. */ + name = NULL; + for (i=0; i < DIM (names); i++) { - 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; + 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 - 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++) - { - 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]) + prefix = unix_rootdir (WANTDIR_SOCKET); + if (!prefix) { - *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 (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; + if (strlen (prefix) + 7 >= sizeof prefixbuffer) + { + *r_info |= 1; /* Ooops: Buffer too short to append "/gnupg". */ + goto leave; + } + strcat (prefixbuffer, "/gnupg"); } - 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]; |