diff --git a/g10/ChangeLog b/g10/ChangeLog index d9da39f43..3ab1403f9 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,19 @@ +2002-08-07 David Shaw + + * keyedit.c (menu_revsig): Properly show a uid is revoked without + restarting gpg. This is Debian bug 124219, though their supplied + patch will not do the right thing. + + * main.h, tdbio.c (tdbio_set_dbname), misc.c (removed + check_permissions), keydb.c (keydb_add_resource), g10.c (main, + check_permissions): Significant reworking of the permission check + mechanism. The new behavior is to check everything in the homedir + by checking the homedir itself. If the user wants to put + (possibly shared) keyrings outside the homedir, they are not + checked. The options file and any extension files are checked + wherever they are, as well as their enclosing directories. This + is Debian bug 147760. + 2002-08-06 Stefan Bellon * g10.c (main): Use of EXTSEP_S in new gpg.conf string. diff --git a/g10/g10.c b/g10/g10.c index 8dcf6b40f..545c5b8a2 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -28,6 +28,9 @@ #ifdef HAVE_DOSISH_SYSTEM #include /* for setmode() */ #endif +#ifdef HAVE_STAT +#include /* for stat() */ +#endif #define INCLUDED_BY_MAIN_MODULE 1 #include "packet.h" @@ -832,6 +835,180 @@ static void add_group(char *string) opt.grouplist=item; } +/* We need to check three things. + + 0) The homedir. It must be x00, a directory, and owned by the + user. + + 1) The options file. Okay unless it or its containing directory is + group or other writable or not owned by us. disable exec in this + case. + + 2) Extensions. Same as #2. + + Returns true if the item is unsafe. */ +static int +check_permissions(const char *path,int item) +{ +#if defined(HAVE_STAT) && !defined(HAVE_DOSISH_SYSTEM) + static int homedir_cache=-1; + char *tmppath,*isa,*dir; + struct stat statbuf,dirbuf; + int homedir=0,ret=0,checkonly=0; + int perm=0,own=0,enc_dir_perm=0,enc_dir_own=0; + + if(opt.no_perm_warn) + return 0; + + /* extensions may attach a path */ + if(item==2 && path[0]!=DIRSEP_C) + { + if(strchr(path,DIRSEP_C)) + tmppath=make_filename(path,NULL); + else + tmppath=make_filename(GNUPG_LIBDIR,path,NULL); + } + else + tmppath=m_strdup(path); + + /* If the item is located in the homedir, but isn't the homedir, + don't continue if we already checked the homedir itself. This is + to avoid user confusion with an extra options file warning which + could be rectified if the homedir itself had proper + permissions. */ + if(item!=0 && homedir_cache>-1 && + ascii_memcasecmp(opt.homedir,tmppath,strlen(opt.homedir))==0) + { + ret=homedir_cache; + goto end; + } + + /* It's okay if the file or directory doesn't exist */ + if(stat(tmppath,&statbuf)!=0) + { + ret=0; + goto end; + } + + /* Now check the enclosing directory. Theoretically, we could walk + this test up to the root directory /, but for the sake of sanity, + I'm stopping at one level down. */ + dir=make_dirname(tmppath); + + if(stat(dir,&dirbuf)!=0 || !S_ISDIR(dirbuf.st_mode)) + { + /* Weird error */ + ret=1; + goto end; + } + + m_free(dir); + + /* Assume failure */ + ret=1; + + if(item==0) + { + isa="homedir"; + + /* The homedir must be x00, a directory, and owned by the user. */ + + if(S_ISDIR(statbuf.st_mode)) + { + if(statbuf.st_uid==getuid()) + { + if((statbuf.st_mode & (S_IRWXG|S_IRWXO))==0) + ret=0; + else + perm=1; + } + else + own=1; + + homedir_cache=ret; + } + } + else if(item==1 || item==2) + { + if(item==1) + isa="configuration file"; + else + isa="extension"; + + /* The options or extension file. Okay unless it or its + containing directory is group or other writable or not owned + by us or root. */ + + if(S_ISREG(statbuf.st_mode)) + { + if(statbuf.st_uid==getuid() || statbuf.st_uid==0) + { + if((statbuf.st_mode & (S_IWGRP|S_IWOTH))==0) + { + /* it's not writable, so make sure the enclosing + directory is also not writable */ + if(dirbuf.st_uid==getuid() || dirbuf.st_uid==0) + { + if((dirbuf.st_mode & (S_IWGRP|S_IWOTH))==0) + ret=0; + else + enc_dir_perm=1; + } + else + enc_dir_own=1; + } + else + { + /* it's writable, so the enclosing directory had + better not let people get to it. */ + if(dirbuf.st_uid==getuid() || dirbuf.st_uid==0) + { + if((dirbuf.st_mode & (S_IRWXG|S_IRWXO))==0) + ret=0; + else + perm=enc_dir_perm=1; /* unclear which one to fix! */ + } + else + enc_dir_own=1; + } + } + else + own=1; + } + } + else + BUG(); + + if(!checkonly) + { + if(own) + log_info(_("WARNING: unsafe ownership on %s \"%s\"\n"), + isa,tmppath); + if(perm) + log_info(_("WARNING: unsafe permissions on %s \"%s\"\n"), + isa,tmppath); + if(enc_dir_own) + log_info(_("WARNING: unsafe enclosing directory " + "ownership on %s \"%s\"\n"), + isa,tmppath); + if(enc_dir_perm) + log_info(_("WARNING: unsafe enclosing directory " + "permissions on %s \"%s\"\n"), + isa,tmppath); + } + + end: + m_free(tmppath); + + if(homedir) + homedir_cache=ret; + + return ret; + +#endif /* HAVE_STAT && !HAVE_DOSISH_SYSTEM */ + + return 0; +} int main( int argc, char **argv ) @@ -843,8 +1020,6 @@ main( int argc, char **argv ) char **orig_argv; const char *fname; char *username; - STRLIST unsafe_files=NULL; - STRLIST extensions=NULL; int may_coredump; STRLIST sl, remusr= NULL, locusr=NULL; STRLIST nrings=NULL, sec_nrings=NULL; @@ -945,6 +1120,8 @@ main( int argc, char **argv ) default_config = 0; /* --no-options */ else if( pargs.r_opt == oHomedir ) opt.homedir = pargs.r.ret_str; + else if( pargs.r_opt == oNoPermissionWarn ) + opt.no_perm_warn=1; #ifdef USE_SHM_COPROCESSING else if( pargs.r_opt == oRunAsShmCP ) { /* does not make sense in a options file, we do it here, @@ -1001,13 +1178,14 @@ main( int argc, char **argv ) pargs.argc = &argc; pargs.argv = &argv; pargs.flags= 1; /* do not remove the args */ + + /* By this point we have a homedir, and cannot change it. */ + check_permissions(opt.homedir,0); + next_pass: if( configname ) { - - if(check_permissions(configname,0,1)) + if(check_permissions(configname,1)) { - add_to_strlist(&unsafe_files,configname); - /* If any options file is unsafe, then disable any external programs for keyserver calls or photo IDs. Since the external program to call is set in the options file, a @@ -1206,9 +1384,14 @@ main( int argc, char **argv ) case oAlwaysTrust: opt.always_trust = 1; break; case oLoadExtension: #ifndef __riscos__ - add_to_strlist(&extensions,pargs.r.ret_str); - register_cipher_extension(orig_argc? *orig_argv:NULL, - pargs.r.ret_str); +#ifdef USE_DYNAMIC_LINKING + if(check_permissions(pargs.r.ret_str,2)) + log_info(_("cipher extension \"%s\" not loaded due to " + "unsafe permissions\n"),pargs.r.ret_str); + else + register_cipher_extension(orig_argc? *orig_argv:NULL, + pargs.r.ret_str); +#endif #else /* __riscos__ */ not_implemented("load-extension"); #endif /* __riscos__ */ @@ -1496,28 +1679,6 @@ main( int argc, char **argv ) } #endif - check_permissions(opt.homedir,0,0); - - if(unsafe_files) - { - STRLIST tmp; - - for(tmp=unsafe_files;tmp;tmp=tmp->next) - check_permissions(tmp->d,0,0); - - free_strlist(unsafe_files); - } - - if(extensions) - { - STRLIST tmp; - - for(tmp=extensions;tmp;tmp=tmp->next) - check_permissions(tmp->d,1,0); - - free_strlist(extensions); - } - if( may_coredump && !opt.quiet ) log_info(_("WARNING: program may create a core file!\n")); @@ -1723,7 +1884,6 @@ main( int argc, char **argv ) /* set the random seed file */ if( use_random_seed ) { char *p = make_filename(opt.homedir, "random_seed", NULL ); - check_permissions(p,0,0); set_random_seed_file(p); m_free(p); } diff --git a/g10/keydb.c b/g10/keydb.c index 96d19105a..eb94ef363 100644 --- a/g10/keydb.c +++ b/g10/keydb.c @@ -114,8 +114,6 @@ keydb_add_resource (const char *url, int force, int secret) else filename = m_strdup (resname); - check_permissions(filename,0,0); - if (!force) force = secret? !any_secret : !any_public; diff --git a/g10/keyedit.c b/g10/keyedit.c index 1da939dfd..d9c3df09f 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -2996,7 +2996,10 @@ menu_revsig( KBNODE keyblock ) } changed = 1; /* we changed the keyblock */ update_trust = 1; - + /* Are we revoking our own uid? */ + if(primary_pk->keyid[0]==sig->keyid[0] && + primary_pk->keyid[1]==sig->keyid[1]) + unode->pkt->pkt.user_id->is_revoked=1; pkt = m_alloc_clear( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; diff --git a/g10/main.h b/g10/main.h index 9edccaf21..09d4a93d8 100644 --- a/g10/main.h +++ b/g10/main.h @@ -71,7 +71,6 @@ int openpgp_cipher_test_algo( int algo ); int openpgp_pk_test_algo( int algo, unsigned int usage_flags ); int openpgp_pk_algo_usage ( int algo ); int openpgp_md_test_algo( int algo ); -int check_permissions(const char *path,int extension,int checkonly); void idea_cipher_warn( int show ); struct expando_args diff --git a/g10/misc.c b/g10/misc.c index 99c6076c5..b0e9543ab 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -24,9 +24,6 @@ #include #include #include -#ifdef HAVE_STAT -#include -#endif #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 #include #include @@ -338,100 +335,6 @@ openpgp_md_test_algo( int algo ) return check_digest_algo(algo); } -int -check_permissions(const char *path,int extension,int checkonly) -{ -#if defined(HAVE_STAT) && !defined(HAVE_DOSISH_SYSTEM) - char *tmppath; - struct stat statbuf; - int ret=1; - int isdir=0; - - if(opt.no_perm_warn) - return 0; - - if(extension && path[0]!=DIRSEP_C) - { - if(strchr(path,DIRSEP_C)) - tmppath=make_filename(path,NULL); - else - tmppath=make_filename(GNUPG_LIBDIR,path,NULL); - } - else - tmppath=m_strdup(path); - - /* It's okay if the file doesn't exist */ - if(stat(tmppath,&statbuf)!=0) - { - ret=0; - goto end; - } - - isdir=S_ISDIR(statbuf.st_mode); - - /* We may have to revisit this if we start piping keyrings to gpg - over a named pipe or keyserver character device :) */ - if(!isdir && !S_ISREG(statbuf.st_mode)) - { - ret=0; - goto end; - } - - /* Per-user files must be owned by the user. Extensions must be - owned by the user or root. */ - if((!extension && statbuf.st_uid != getuid()) || - (extension && statbuf.st_uid!=0 && statbuf.st_uid!=getuid())) - { - if(!checkonly) - log_info(_("WARNING: unsafe ownership on %s \"%s\"\n"), - isdir?"directory":extension?"extension":"file",path); - goto end; - } - - /* This works for both directories and files - basically, we don't - care what the owner permissions are, so long as the group and - other permissions are 0 for per-user files, and non-writable for - extensions. */ - if((extension && (statbuf.st_mode & (S_IWGRP|S_IWOTH)) !=0) || - (!extension && (statbuf.st_mode & (S_IRWXG|S_IRWXO)) != 0)) - { - char *dir; - - /* However, if the directory the directory/file is in is owned - by the user and is 700, then this is not a problem. - Theoretically, we could walk this test up to the root - directory /, but for the sake of sanity, I'm stopping at one - level down. */ - - dir=make_dirname(tmppath); - if(stat(dir,&statbuf)==0 && statbuf.st_uid==getuid() && - S_ISDIR(statbuf.st_mode) && (statbuf.st_mode & (S_IRWXG|S_IRWXO))==0) - { - m_free(dir); - ret=0; - goto end; - } - - m_free(dir); - - if(!checkonly) - log_info(_("WARNING: unsafe permissions on %s \"%s\"\n"), - isdir?"directory":extension?"extension":"file",path); - goto end; - } - - ret=0; - - end: - m_free(tmppath); - - return ret; - -#endif /* HAVE_STAT && !HAVE_DOSISH_SYSTEM */ - - return 0; -} - /* Special warning for the IDEA cipher */ void idea_cipher_warn(int show) diff --git a/g10/tdbio.c b/g10/tdbio.c index 537e4c0d4..20b32afac 100644 --- a/g10/tdbio.c +++ b/g10/tdbio.c @@ -447,8 +447,6 @@ tdbio_set_dbname( const char *new_dbname, int create ) : make_filename(opt.homedir, "trustdb" EXTSEP_S "gpg", NULL ); - check_permissions(fname,0,0); - if( access( fname, R_OK ) ) { if( errno != ENOENT ) { log_error( _("%s: can't access: %s\n"), fname, strerror(errno) );