/* Create a lock file for the container FNAME and store the lock at * R_LOCK and return 0. On error return an error code and store NULL * at R_LOCK. */ gpg_error_t be_take_lock_for_create (ctrl_t ctrl, const char *fname, dotlock_t *r_lock) { gpg_error_t err; dotlock_t lock = NULL; struct stat sb; *r_lock = NULL; /* A DM-crypt container requires special treatment by using the syshelper fucntions. */ if (ctrl->conttype == CONTTYPE_DM_CRYPT) { /* */ err = call_syshelp_set_device (ctrl, fname); goto leave; } /* A quick check to see that no container with that name already exists. */ if (!access (fname, F_OK)) { err = gpg_error (GPG_ERR_EEXIST); goto leave; } /* Take a lock and proceed with the creation. If there is a lock we immediately return an error because for creation it does not make sense to wait. */ lock = dotlock_create (fname, 0); if (!lock) { err = gpg_error_from_syserror (); goto leave; } if (dotlock_take (lock, 0)) { err = gpg_error_from_syserror (); goto leave; } /* Check again that the file does not exist. */ err = stat (fname, &sb)? 0 : gpg_error (GPG_ERR_EEXIST); leave: if (!err) { *r_lock = lock; lock = NULL; } dotlock_destroy (lock); return err; }
/* Unlock the spawning process. */ static void unlock_spawning (lock_spawn_t *lock, const char *name) { if (*lock) { #ifdef HAVE_W32_SYSTEM if (!ReleaseMutex (*lock)) log_error ("failed to release the spawn_%s mutex: %s\n", name, w32_strerror (-1)); CloseHandle (*lock); #else /*!HAVE_W32_SYSTEM*/ (void)name; dotlock_destroy (*lock); #endif /*!HAVE_W32_SYSTEM*/ *lock = NULL; } }
/* Remove all lockfiles. This is called by the atexit handler installed by this module but may also be called by other termination handlers. */ void dotlock_remove_lockfiles (void) { dotlock_t h, h2; /* First set the lockfiles list to NULL so that for example dotlock_release is ware that this fucntion is currently running. */ LOCK_all_lockfiles (); h = all_lockfiles; all_lockfiles = NULL; UNLOCK_all_lockfiles (); while ( h ) { h2 = h->next; dotlock_destroy (h); h = h2; } }
/* Handle the creation of a keyring or a keybox if it does not yet exist. Take into acount that other processes might have the keyring/keybox already locked. This lock check does not work if the directory itself is not yet available. */ static int maybe_create_keyring_or_box (char *filename, int is_box, int force) { dotlock_t lockhd = NULL; IOBUF iobuf; int rc; mode_t oldmask; char *last_slash_in_filename; int save_slash; /* A quick test whether the filename already exists. */ if (!access (filename, F_OK)) return 0; /* If we don't want to create a new file at all, there is no need to go any further - bail out right here. */ if (!force) return gpg_error (GPG_ERR_ENOENT); /* First of all we try to create the home directory. Note, that we don't do any locking here because any sane application of gpg would create the home directory by itself and not rely on gpg's tricky auto-creation which is anyway only done for some home directory name patterns. */ last_slash_in_filename = strrchr (filename, DIRSEP_C); #if HAVE_W32_SYSTEM { /* Windows may either have a slash or a backslash. Take care of it. */ char *p = strrchr (filename, '/'); if (!last_slash_in_filename || p > last_slash_in_filename) last_slash_in_filename = p; } #endif /*HAVE_W32_SYSTEM*/ if (!last_slash_in_filename) return gpg_error (GPG_ERR_ENOENT); /* No slash at all - should not happen though. */ save_slash = *last_slash_in_filename; *last_slash_in_filename = 0; if (access(filename, F_OK)) { static int tried; if (!tried) { tried = 1; try_make_homedir (filename); } if (access (filename, F_OK)) { rc = gpg_error_from_syserror (); *last_slash_in_filename = save_slash; goto leave; } } *last_slash_in_filename = save_slash; /* To avoid races with other instances of gpg trying to create or update the keyring (it is removed during an update for a short time), we do the next stuff in a locked state. */ lockhd = dotlock_create (filename, 0); if (!lockhd) { rc = gpg_error_from_syserror (); /* A reason for this to fail is that the directory is not writable. However, this whole locking stuff does not make sense if this is the case. An empty non-writable directory with no keyring is not really useful at all. */ if (opt.verbose) log_info ("can't allocate lock for '%s': %s\n", filename, gpg_strerror (rc)); if (!force) return gpg_error (GPG_ERR_ENOENT); else return rc; } if ( dotlock_take (lockhd, -1) ) { rc = gpg_error_from_syserror (); /* This is something bad. Probably a stale lockfile. */ log_info ("can't lock '%s': %s\n", filename, gpg_strerror (rc)); goto leave; } /* Now the real test while we are locked. */ if (!access (filename, F_OK)) { rc = 0; /* Okay, we may access the file now. */ goto leave; } /* The file does not yet exist, create it now. */ oldmask = umask (077); if (is_secured_filename (filename)) { iobuf = NULL; gpg_err_set_errno (EPERM); } else iobuf = iobuf_create (filename); umask (oldmask); if (!iobuf) { rc = gpg_error_from_syserror (); if (is_box) log_error (_("error creating keybox '%s': %s\n"), filename, gpg_strerror (rc)); else log_error (_("error creating keyring '%s': %s\n"), filename, gpg_strerror (rc)); goto leave; } iobuf_close (iobuf); /* Must invalidate that ugly cache */ iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, filename); /* Make sure that at least one record is in a new keybox file, so that the detection magic will work the next time it is used. */ if (is_box) { FILE *fp = fopen (filename, "w"); if (!fp) rc = gpg_error_from_syserror (); else { rc = _keybox_write_header_blob (fp); fclose (fp); } if (rc) { if (is_box) log_error (_("error creating keybox '%s': %s\n"), filename, gpg_strerror (rc)); else log_error (_("error creating keyring '%s': %s\n"), filename, gpg_strerror (rc)); goto leave; } } if (!opt.quiet) { if (is_box) log_info (_("keybox '%s' created\n"), filename); else log_info (_("keyring '%s' created\n"), filename); } rc = 0; leave: if (lockhd) { dotlock_release (lockhd); dotlock_destroy (lockhd); } return rc; }
/* Mount the container with name FILENAME at MOUNTPOINT. */ gpg_error_t g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint) { gpg_error_t err; dotlock_t lock; void *enckeyblob = NULL; size_t enckeybloblen; void *keyblob = NULL; size_t keybloblen; tupledesc_t tuples = NULL; size_t n; const unsigned char *value; int conttype; unsigned int rid; char *mountpoint_buffer = NULL; /* A quick check to see whether the container exists. */ if (access (filename, R_OK)) return gpg_error_from_syserror (); if (!mountpoint) { mountpoint_buffer = xtrystrdup ("/tmp/g13-XXXXXX"); if (!mountpoint_buffer) return gpg_error_from_syserror (); if (!gnupg_mkdtemp (mountpoint_buffer)) { err = gpg_error_from_syserror (); log_error (_("can't create directory '%s': %s\n"), "/tmp/g13-XXXXXX", gpg_strerror (err)); xfree (mountpoint_buffer); return err; } mountpoint = mountpoint_buffer; } /* Try to take a lock. */ lock = dotlock_create (filename, 0); if (!lock) { xfree (mountpoint_buffer); return gpg_error_from_syserror (); } if (dotlock_take (lock, 0)) { err = gpg_error_from_syserror (); goto leave; } else err = 0; /* Check again that the file exists. */ { struct stat sb; if (stat (filename, &sb)) { err = gpg_error_from_syserror (); goto leave; } } /* Read the encrypted keyblob. */ err = read_keyblob (filename, &enckeyblob, &enckeybloblen); if (err) goto leave; /* Decrypt that keyblob and store it in a tuple descriptor. */ err = decrypt_keyblob (ctrl, enckeyblob, enckeybloblen, &keyblob, &keybloblen); if (err) goto leave; xfree (enckeyblob); enckeyblob = NULL; err = create_tupledesc (&tuples, keyblob, keybloblen); if (!err) keyblob = NULL; else { if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED) log_error ("unknown keyblob version\n"); goto leave; } if (opt.verbose) dump_keyblob (tuples); value = find_tuple (tuples, KEYBLOB_TAG_CONTTYPE, &n); if (!value || n != 2) conttype = 0; else conttype = (value[0] << 8 | value[1]); if (!be_is_supported_conttype (conttype)) { log_error ("content type %d is not supported\n", conttype); err = gpg_error (GPG_ERR_NOT_SUPPORTED); goto leave; } err = be_mount_container (ctrl, conttype, filename, mountpoint, tuples, &rid); if (!err) { err = mountinfo_add_mount (filename, mountpoint, conttype, rid, !!mountpoint_buffer); /* Fixme: What shall we do if this fails? Add a provisional mountinfo entry first and remove it on error? */ if (!err) { char *tmp = percent_plus_escape (mountpoint); if (!tmp) err = gpg_error_from_syserror (); else { g13_status (ctrl, STATUS_MOUNTPOINT, tmp, NULL); xfree (tmp); } } } leave: destroy_tupledesc (tuples); xfree (keyblob); xfree (enckeyblob); dotlock_destroy (lock); xfree (mountpoint_buffer); return err; }
/* Check whether a default secring.gpg from GnuPG < 2.1 exists and import it if not yet done. */ void migrate_secring (ctrl_t ctrl) { dotlock_t lockhd = NULL; char *secring = NULL; char *flagfile = NULL; char *agent_version = NULL; secring = make_filename (opt.homedir, "secring" EXTSEP_S "gpg", NULL); if (access (secring, F_OK)) goto leave; /* Does not exist or is not readable. */ flagfile = make_filename (opt.homedir, V21_MIGRATION_FNAME, NULL); if (!access (flagfile, F_OK)) goto leave; /* Does exist - fine. */ log_info ("starting migration from earlier GnuPG versions\n"); lockhd = dotlock_create (flagfile, 0); if (!lockhd) { log_error ("can't allocate lock for '%s': %s\n", flagfile, gpg_strerror (gpg_error_from_syserror ())); goto leave; } if (dotlock_take (lockhd, -1)) { log_error ("can't lock '%s': %s\n", flagfile, gpg_strerror (gpg_error_from_syserror ())); dotlock_destroy (lockhd); lockhd = NULL; goto leave; } if (!agent_get_version (ctrl, &agent_version)) { if (!gnupg_compare_version (agent_version, "2.1.0")) { log_error ("error: GnuPG agent version \"%s\" is too old. ", agent_version); log_info ("Please make sure that a recent gpg-agent is running.\n"); log_info ("(restarting the user session may achieve this.)\n"); log_info ("migration aborted\n"); xfree (agent_version); goto leave; } xfree (agent_version); } else { log_error ("error: GnuPG agent unusable. " "Please check that a GnuPG agent can be started.\n"); log_error ("migration aborted\n"); goto leave; } log_info ("porting secret keys from '%s' to gpg-agent\n", secring); if (!import_old_secring (ctrl, secring)) { FILE *fp = fopen (flagfile, "w"); if (!fp || fclose (fp)) log_error ("error creating flag file '%s': %s\n", flagfile, gpg_strerror (gpg_error_from_syserror ())); else log_info ("migration succeeded\n"); } leave: if (lockhd) { dotlock_release (lockhd); dotlock_destroy (lockhd); } xfree (flagfile); xfree (secring); }