diff --git a/sm/gpgsm.c b/sm/gpgsm.c index ded31986d..2faf203dd 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -969,7 +969,10 @@ main ( int argc, char **argv) default_config = 0; } else if (pargs.r_opt == oNoOptions) - default_config = 0; /* --no-options */ + { + default_config = 0; /* --no-options */ + opt.no_homedir_creation = 1; + } else if (pargs.r_opt == oHomedir) opt.homedir = pargs.r.ret_str; else if (pargs.r_opt == aCallProtectTool) @@ -1270,7 +1273,7 @@ main ( int argc, char **argv) goto next_pass; } break; - case oNoOptions: break; /* no-options */ + case oNoOptions: opt.no_homedir_creation = 1; break; /* no-options */ case oHomedir: opt.homedir = pargs.r.ret_str; break; case oAgentProgram: opt.agent_program = pargs.r.ret_str; break; diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 83918cc9a..e8322b731 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -59,6 +59,7 @@ struct int answer_yes; /* assume yes on most questions */ int answer_no; /* assume no on most questions */ int dry_run; /* don't change any persistent data */ + int no_homedir_creation; const char *homedir; /* Configuration directory name */ const char *config_filename; /* Name of the used config file. */ diff --git a/sm/keydb.c b/sm/keydb.c index d9eb2e073..5a250b020 100644 --- a/sm/keydb.c +++ b/sm/keydb.c @@ -1,5 +1,6 @@ /* keydb.c - key database dispatcher * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. + * Copyright (C) 2014 g10 Code GmbH * * This file is part of GnuPG. * @@ -68,11 +69,173 @@ static int lock_all (KEYDB_HANDLE hd); static void unlock_all (KEYDB_HANDLE hd); +static void +try_make_homedir (const char *fname) +{ + const char *defhome = standard_homedir (); + + /* Create the directory only if the supplied directory name is the + same as the default one. This way we avoid to create arbitrary + directories when a non-default home directory is used. To cope + with HOME, we do compare only the suffix if we see that the + default homedir does start with a tilde. */ + if ( opt.dry_run || opt.no_homedir_creation ) + return; + + if ( +#ifdef HAVE_W32_SYSTEM + ( !compare_filenames (fname, defhome) ) +#else + ( *defhome == '~' + && (strlen(fname) >= strlen (defhome+1) + && !strcmp(fname+strlen(fname)-strlen(defhome+1), defhome+1 ) )) + || (*defhome != '~' && !compare_filenames( fname, defhome ) ) +#endif + ) + { + if (gnupg_mkdir (fname, "-rwx")) + log_info (_("can't create directory '%s': %s\n"), + fname, strerror(errno) ); + else if (!opt.quiet ) + log_info (_("directory '%s' created\n"), fname); + } +} + + +/* Handle the creation of a keybox if it does not yet exist. Take + into acount that other processes might have the keybox already + locked. This lock check does not work if the directory itself is + not yet available. If R_CREATED is not NULL it will be set to true + if the function created a new keybox. */ +static int +maybe_create_keybox (char *filename, int force, int *r_created) +{ + dotlock_t lockhd = NULL; + FILE *fp; + int rc; + mode_t oldmask; + char *last_slash_in_filename; + int save_slash; + + if (r_created) + *r_created = 0; + + /* 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 keybox (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) + { + /* 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'\n", filename ); + + if (!force) + return gpg_error (GPG_ERR_ENOENT); + else + return gpg_error (GPG_ERR_GENERAL); + } + + if ( dotlock_take (lockhd, -1) ) + { + /* This is something bad. Probably a stale lockfile. */ + log_info ("can't lock '%s'\n", filename); + rc = gpg_error (GPG_ERR_GENERAL); + 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); + fp = fopen (filename, "w"); + if (!fp) + { + rc = gpg_error_from_syserror (); + umask (oldmask); + log_error (_("error creating keybox '%s': %s\n"), + filename, gpg_strerror (rc)); + goto leave; + } + umask (oldmask); + + if (!opt.quiet) + log_info (_("keybox '%s' created\n"), filename); + if (r_created) + *r_created = 1; + + fclose (fp); + rc = 0; + + leave: + if (lockhd) + { + dotlock_release (lockhd); + dotlock_destroy (lockhd); + } + return rc; +} + + /* * Register a resource (which currently may only be a keybox file). * The first keybox which is added by this function is created if it * does not exist. If AUTO_CREATED is not NULL it will be set to true - * if the function has created a a new keybox. + * if the function has created a new keybox. */ int keydb_add_resource (const char *url, int force, int secret, int *auto_created) @@ -81,7 +244,6 @@ keydb_add_resource (const char *url, int force, int secret, int *auto_created) const char *resname = url; char *filename = NULL; int rc = 0; - FILE *fp; KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE; if (auto_created) @@ -124,24 +286,25 @@ keydb_add_resource (const char *url, int force, int secret, int *auto_created) /* see whether we can determine the filetype */ if (rt == KEYDB_RESOURCE_TYPE_NONE) { - FILE *fp2 = fopen( filename, "rb" ); + FILE *fp = fopen( filename, "rb" ); - if (fp2) { - u32 magic; + if (fp) + { + u32 magic; - /* FIXME: check for the keybox magic */ - if (fread( &magic, 4, 1, fp2) == 1 ) - { - if (magic == 0x13579ace || magic == 0xce9a5713) - ; /* GDBM magic - no more support */ - else - rt = KEYDB_RESOURCE_TYPE_KEYBOX; - } - else /* maybe empty: assume ring */ - rt = KEYDB_RESOURCE_TYPE_KEYBOX; - fclose (fp2); - } - else /* no file yet: create ring */ + /* FIXME: check for the keybox magic */ + if (fread (&magic, 4, 1, fp) == 1 ) + { + if (magic == 0x13579ace || magic == 0xce9a5713) + ; /* GDBM magic - no more support */ + else + rt = KEYDB_RESOURCE_TYPE_KEYBOX; + } + else /* maybe empty: assume keybox */ + rt = KEYDB_RESOURCE_TYPE_KEYBOX; + fclose (fp); + } + else /* no file yet: create keybox */ rt = KEYDB_RESOURCE_TYPE_KEYBOX; } @@ -153,91 +316,46 @@ keydb_add_resource (const char *url, int force, int secret, int *auto_created) goto leave; case KEYDB_RESOURCE_TYPE_KEYBOX: - fp = fopen (filename, "rb"); - if (!fp && !force) - { - rc = gpg_error (gpg_err_code_from_errno (errno)); - goto leave; - } - - if (!fp) - { /* no file */ -#if 0 /* no autocreate of the homedirectory yet */ + rc = maybe_create_keybox (filename, force, auto_created); + if (rc) + goto leave; + /* Now register the file */ + { + void *token = keybox_register_file (filename, secret); + if (!token) + ; /* already registered - ignore it */ + else if (used_resources >= MAX_KEYDB_RESOURCES) + rc = gpg_error (GPG_ERR_RESOURCE_LIMIT); + else { - char *last_slash_in_filename; + all_resources[used_resources].type = rt; + all_resources[used_resources].u.kr = NULL; /* Not used here */ + all_resources[used_resources].token = token; + all_resources[used_resources].secret = secret; - last_slash_in_filename = strrchr (filename, DIRSEP_C); - *last_slash_in_filename = 0; - if (access (filename, F_OK)) - { /* on the first time we try to create the default - homedir and in this case the process will be - terminated, so that on the next invocation can - read the options file in on startup */ - try_make_homedir (filename); - rc = gpg_error (GPG_ERR_FILE_OPEN_ERROR); - *last_slash_in_filename = DIRSEP_C; - goto leave; + all_resources[used_resources].lockhandle + = dotlock_create (filename, 0); + if (!all_resources[used_resources].lockhandle) + log_fatal ( _("can't create lock for '%s'\n"), filename); + + /* Do a compress run if needed and the file is not locked. */ + if (!dotlock_take (all_resources[used_resources].lockhandle, 0)) + { + KEYBOX_HANDLE kbxhd = keybox_new (token, secret); + + if (kbxhd) + { + keybox_compress (kbxhd); + keybox_release (kbxhd); + } + dotlock_release (all_resources[used_resources].lockhandle); } - *last_slash_in_filename = DIRSEP_C; + + used_resources++; } -#endif - fp = fopen (filename, "w"); - if (!fp) - { - rc = gpg_error (gpg_err_code_from_errno (errno)); - log_error (_("error creating keybox '%s': %s\n"), - filename, strerror(errno)); - if (errno == ENOENT) - log_info (_("you may want to start the gpg-agent first\n")); - goto leave; - } + } + break; - if (!opt.quiet) - log_info (_("keybox '%s' created\n"), filename); - if (auto_created) - *auto_created = 1; - } - fclose (fp); - fp = NULL; - /* now register the file */ - { - - void *token = keybox_register_file (filename, secret); - if (!token) - ; /* already registered - ignore it */ - else if (used_resources >= MAX_KEYDB_RESOURCES) - rc = gpg_error (GPG_ERR_RESOURCE_LIMIT); - else - { - all_resources[used_resources].type = rt; - all_resources[used_resources].u.kr = NULL; /* Not used here */ - all_resources[used_resources].token = token; - all_resources[used_resources].secret = secret; - - all_resources[used_resources].lockhandle - = dotlock_create (filename, 0); - if (!all_resources[used_resources].lockhandle) - log_fatal ( _("can't create lock for '%s'\n"), filename); - - /* Do a compress run if needed and the file is not locked. */ - if (!dotlock_take (all_resources[used_resources].lockhandle, 0)) - { - KEYBOX_HANDLE kbxhd = keybox_new (token, secret); - - if (kbxhd) - { - keybox_compress (kbxhd); - keybox_release (kbxhd); - } - dotlock_release (all_resources[used_resources].lockhandle); - } - - used_resources++; - } - } - - - break; default: log_error ("resource type of '%s' not supported\n", url); rc = gpg_error (GPG_ERR_NOT_SUPPORTED);