diff options
Diffstat (limited to 'g10/misc.c')
-rw-r--r-- | g10/misc.c | 704 |
1 files changed, 456 insertions, 248 deletions
diff --git a/g10/misc.c b/g10/misc.c index 2348e46f0..c2330d959 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -1,5 +1,5 @@ /* misc.c - miscellaneous functions - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -22,28 +22,44 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> #include <errno.h> +#ifdef HAVE_STAT +#include <sys/stat.h> +#endif #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 #include <asm/sysinfo.h> #include <asm/unistd.h> #endif #ifdef HAVE_SETRLIMIT + #include <time.h> #include <sys/time.h> #include <sys/resource.h> #endif -#include <assert.h> - -#include <gcrypt.h> #include "util.h" #include "main.h" +#include "photoid.h" #include "options.h" #include "i18n.h" -#define MAX_EXTERN_MPI_BITS 16384 +const char *g10m_revision_string(int); +const char *g10c_revision_string(int); +const char *g10u_revision_string(int); + +#ifdef __GNUC__ +volatile +#endif + void +pull_in_libs(void) +{ + g10m_revision_string(0); + g10c_revision_string(0); + g10u_revision_string(0); +} + #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 -#warning using trap_unaligned static int setsysinfo(unsigned long op, void *buffer, unsigned long size, int *start, void *arg, unsigned long flag) @@ -68,7 +84,6 @@ trap_unaligned(void) #endif - int disable_core_dumps() { @@ -91,166 +106,23 @@ disable_core_dumps() -/**************** - * write an mpi to out. - */ -int -mpi_write( IOBUF out, MPI a ) -{ - char buffer[(MAX_EXTERN_MPI_BITS+7)/8]; - size_t nbytes; - int rc; - - nbytes = (MAX_EXTERN_MPI_BITS+7)/8; - rc = gcry_mpi_print( GCRYMPI_FMT_PGP, buffer, &nbytes, a ); - if( !rc ) - rc = iobuf_write( out, buffer, nbytes ); - - return rc; -} - -/**************** - * Writye a MPI to out, but in this case it is an opaque one, - * s used vor v3 protected keys. - */ -int -mpi_write_opaque( IOBUF out, MPI a ) -{ - size_t nbytes, nbits; - int rc; - char *p; - - assert( gcry_mpi_get_flag( a, GCRYMPI_FLAG_OPAQUE ) ); - p = gcry_mpi_get_opaque( a, &nbits ); - nbytes = (nbits+7) / 8; - iobuf_put( out, nbits >> 8 ); - iobuf_put( out, nbits ); - rc = iobuf_write( out, p, nbytes ); - return rc; -} - - -/**************** - * Read an external representation of an mpi and return the MPI - * The external format is a 16 bit unsigned value stored in network byte order, - * giving the number of bits for the following integer. The integer is stored - * with MSB first (left padded with zeroes to align on a byte boundary). - */ -MPI -mpi_read(IOBUF inp, unsigned int *ret_nread, int secure) +u16 +checksum_u16( unsigned n ) { - int c, c1, c2, i; - unsigned int nbits, nbytes, nread=0; - MPI a = NULL; - byte *buf = NULL; - byte *p; - - if( (c = c1 = iobuf_get(inp)) == -1 ) - goto leave; - nbits = c << 8; - if( (c = c2 = iobuf_get(inp)) == -1 ) - goto leave; - nbits |= c; - if( nbits > MAX_EXTERN_MPI_BITS ) { - log_error("mpi too large (%u bits)\n", nbits); - goto leave; - } - nread = 2; - nbytes = (nbits+7) / 8; - buf = secure? gcry_xmalloc_secure( nbytes+2 ) : gcry_xmalloc( nbytes+2 ); - p = buf; - p[0] = c1; - p[1] = c2; - for( i=0 ; i < nbytes; i++ ) { - p[i+2] = iobuf_get(inp) & 0xff; - nread++; - } - nread += nbytes; - if( gcry_mpi_scan( &a, GCRYMPI_FMT_PGP, buf, &nread ) ) - a = NULL; - - leave: - gcry_free(buf); - if( nread > *ret_nread ) - log_bug("mpi larger than packet"); - else - *ret_nread = nread; - return a; -} + u16 a; -/**************** - * Same as mpi_read but the value is stored as an opaque MPI. - * This function is used to read encrypted MPI of v3 packets. - */ -GCRY_MPI -mpi_read_opaque(IOBUF inp, unsigned *ret_nread ) -{ - int c, c1, c2, i; - unsigned nbits, nbytes, nread=0; - GCRY_MPI a = NULL; - byte *buf = NULL; - byte *p; - - if( (c = c1 = iobuf_get(inp)) == -1 ) - goto leave; - nbits = c << 8; - if( (c = c2 = iobuf_get(inp)) == -1 ) - goto leave; - nbits |= c; - if( nbits > MAX_EXTERN_MPI_BITS ) { - log_error("mpi too large (%u bits)\n", nbits); - goto leave; - } - nread = 2; - nbytes = (nbits+7) / 8; - buf = gcry_xmalloc( nbytes ); - p = buf; - for( i=0 ; i < nbytes; i++ ) { - p[i] = iobuf_get(inp) & 0xff; + a = (n >> 8) & 0xff; + if( opt.emulate_bugs & EMUBUG_GPGCHKSUM ) { + a |= n & 0xff; + log_debug("csum_u16 emulated for n=%u\n", n); } - nread += nbytes; - a = gcry_mpi_set_opaque(NULL, buf, nbits ); - buf = NULL; - - leave: - gcry_free(buf); - if( nread > *ret_nread ) - log_bug("mpi larger than packet"); else - *ret_nread = nread; + a += n & 0xff; return a; } - -int -mpi_print( FILE *fp, MPI a, int mode ) -{ - int n=0; - - if( !a ) - return fprintf(fp, "[MPI_NULL]"); - if( !mode ) { - unsigned int n1; - n1 = gcry_mpi_get_nbits(a); - n += fprintf(fp, "[%u bits]", n1); - } - else { - int rc; - char *buffer; - - rc = gcry_mpi_aprint( GCRYMPI_FMT_HEX, (void **)&buffer, NULL, a ); - assert( !rc ); - fputs( buffer, fp ); - n += strlen(buffer); - gcry_free( buffer ); - } - return n; -} - - - -u16 -checksum_u16( unsigned n ) +static u16 +checksum_u16_nobug( unsigned n ) { u16 a; @@ -272,22 +144,47 @@ checksum( byte *p, unsigned n ) u16 checksum_mpi( MPI a ) { - int rc; u16 csum; byte *buffer; - size_t nbytes; - - rc = gcry_mpi_print( GCRYMPI_FMT_PGP, NULL, &nbytes, a ); - assert( !rc ); - /* fixme: for numbers not in the suecre memory we - * should use a stack based buffer and only allocate - * a larger one when the mpi_print return an error + unsigned nbytes; + unsigned nbits; + + buffer = mpi_get_buffer( a, &nbytes, NULL ); + /* some versions of gpg encode wrong values for the length of an mpi + * so that mpi_get_nbits() which counts the mpi yields another (shorter) + * value than the one store with the mpi. mpi_get_nbit_info() returns + * this stored value if it is still available. */ - buffer = gcry_is_secure(a)? gcry_xmalloc_secure(nbytes) : gcry_xmalloc(nbytes); - rc = gcry_mpi_print( GCRYMPI_FMT_PGP, buffer, &nbytes, a ); - assert( !rc ); - csum = checksum( buffer, nbytes ); - gcry_free( buffer ); + + if( opt.emulate_bugs & EMUBUG_GPGCHKSUM ) + nbits = 0; + else + nbits = mpi_get_nbit_info(a); + if( !nbits ) + nbits = mpi_get_nbits(a); + csum = checksum_u16( nbits ); + csum += checksum( buffer, nbytes ); + m_free( buffer ); + return csum; +} + +/**************** + * This is the correct function + */ +u16 +checksum_mpi_counted_nbits( MPI a ) +{ + u16 csum; + byte *buffer; + unsigned nbytes; + unsigned nbits; + + buffer = mpi_get_buffer( a, &nbytes, NULL ); + nbits = mpi_get_nbits(a); + mpi_set_nbit_info(a,nbits); + csum = checksum_u16_nobug( nbits ); + csum += checksum( buffer, nbytes ); + m_free( buffer ); return csum; } @@ -327,11 +224,13 @@ print_cipher_algo_note( int algo ) { if( algo >= 100 && algo <= 110 ) no_exp_algo(); - else if( algo == GCRY_CIPHER_3DES - || algo == GCRY_CIPHER_CAST5 - || algo == GCRY_CIPHER_BLOWFISH - || algo == GCRY_CIPHER_RIJNDAEL - || algo == GCRY_CIPHER_TWOFISH + else if( algo == CIPHER_ALGO_3DES + || algo == CIPHER_ALGO_CAST5 + || algo == CIPHER_ALGO_BLOWFISH + || algo == CIPHER_ALGO_TWOFISH + || algo == CIPHER_ALGO_RIJNDAEL + || algo == CIPHER_ALGO_RIJNDAEL192 + || algo == CIPHER_ALGO_RIJNDAEL256 ) ; else { @@ -339,7 +238,7 @@ print_cipher_algo_note( int algo ) if( !did_note ) { did_note = 1; - log_info(_("this cipher algorithm is depreciated; " + log_info(_("this cipher algorithm is deprecated; " "please use a more standard one!\n")); } } @@ -353,6 +252,32 @@ print_digest_algo_note( int algo ) } +/* Return a string which is used as a kind of process ID */ +const byte * +get_session_marker( size_t *rlen ) +{ + static byte marker[SIZEOF_UNSIGNED_LONG*2]; + static int initialized; + + if ( !initialized ) { + volatile ulong aa, bb; /* we really want the uninitialized value */ + ulong a, b; + + initialized = 1; + /* also this marker is guessable it is not easy to use this + * for a faked control packet because an attacker does not + * have enough control about the time the verification does + * take place. Of course, we can add just more random but + * than we need the random generator even for verification + * tasks - which does not make sense. */ + a = aa ^ (ulong)getpid(); + b = bb ^ (ulong)time(NULL); + memcpy( marker, &a, SIZEOF_UNSIGNED_LONG ); + memcpy( marker+SIZEOF_UNSIGNED_LONG, &b, SIZEOF_UNSIGNED_LONG ); + } + *rlen = sizeof(marker); + return marker; +} /**************** * Wrapper around the libgcrypt function with addional checks on @@ -362,122 +287,405 @@ int openpgp_cipher_test_algo( int algo ) { if( algo < 0 || algo > 110 ) - return GCRYERR_INV_CIPHER_ALGO; - return gcry_cipher_test_algo(algo); + return G10ERR_CIPHER_ALGO; + return check_cipher_algo(algo); } int openpgp_pk_test_algo( int algo, unsigned int usage_flags ) { - size_t n = usage_flags; - if( algo < 0 || algo > 110 ) - return GCRYERR_INV_PK_ALGO; - return gcry_pk_algo_info( algo, GCRYCTL_TEST_ALGO, NULL, &n ); + return G10ERR_PUBKEY_ALGO; + return check_pubkey_algo2( algo, usage_flags ); } int openpgp_pk_algo_usage ( int algo ) { - int usage = 0; + int use = 0; - /* some are hardwired */ + /* they are hardwired in gpg 1.0 */ switch ( algo ) { - case GCRY_PK_RSA: - usage = GCRY_PK_USAGE_SIGN | GCRY_PK_USAGE_ENCR; + case PUBKEY_ALGO_RSA: + use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC; break; - case GCRY_PK_RSA_E: - usage = GCRY_PK_USAGE_ENCR; + case PUBKEY_ALGO_RSA_E: + use = PUBKEY_USAGE_ENC; break; - case GCRY_PK_RSA_S: - usage = GCRY_PK_USAGE_SIGN; + case PUBKEY_ALGO_RSA_S: + use = PUBKEY_USAGE_SIG; break; - case GCRY_PK_ELG_E: - usage = GCRY_PK_USAGE_ENCR; + case PUBKEY_ALGO_ELGAMAL_E: + use = PUBKEY_USAGE_ENC; break; - case GCRY_PK_DSA: - usage = GCRY_PK_USAGE_SIGN; + case PUBKEY_ALGO_DSA: + use = PUBKEY_USAGE_SIG; break; - case GCRY_PK_ELG: - usage = GCRY_PK_USAGE_SIGN | GCRY_PK_USAGE_ENCR; + case PUBKEY_ALGO_ELGAMAL: + use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC; break; default: - usage = gcry_pk_algo_info ( algo, GCRYCTL_GET_ALGO_USAGE, - NULL, NULL); + break; } - return usage; - + return use; } - - int openpgp_md_test_algo( int algo ) { if( algo < 0 || algo > 110 ) - return GCRYERR_INV_MD_ALGO; - return gcry_md_test_algo(algo); + return G10ERR_DIGEST_ALGO; + return check_digest_algo(algo); } - int -pubkey_get_npkey( int algo ) +check_permissions(const char *path,int extension,int checkonly) { - int n = gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NPKEY, NULL, 0 ); - return n > 0? n : 0; +#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; } -int -pubkey_get_nskey( int algo ) +/* Special warning for the IDEA cipher */ +void +idea_cipher_warn(int show) { - int n = gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSKEY, NULL, 0 ); - return n > 0? n : 0; + static int warned=0; + + if(!warned || show) + { + log_info(_("the IDEA cipher plugin is not present\n")); + log_info(_("please see http://www.gnupg.org/why-not-idea.html " + "for more information\n")); + warned=1; + } } -int -pubkey_get_nsig( int algo ) +/* Expand %-strings. Returns a string which must be m_freed. Returns + NULL if the string cannot be expanded (too large). */ +char * +pct_expando(const char *string,struct expando_args *args) { - int n = gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSIGN, NULL, 0 ); - return n > 0? n : 0; + const char *ch=string; + int idx=0,maxlen=0,done=0; + u32 pk_keyid[2]={0,0},sk_keyid[2]={0,0}; + char *ret=NULL; + + if(args->pk) + keyid_from_pk(args->pk,pk_keyid); + + if(args->sk) + keyid_from_sk(args->sk,sk_keyid); + + if(!args->pk && args->sk) + keyid_from_sk(args->sk,pk_keyid); + + while(*ch!='\0') + { + char *str=NULL; + + if(!done) + { + /* 8192 is way bigger than we'll need here */ + if(maxlen>=8192) + goto fail; + + maxlen+=1024; + ret=m_realloc(ret,maxlen); + } + + done=0; + + if(*ch=='%') + { + switch(*(ch+1)) + { + case 's': /* short key id */ + if(idx+8<maxlen) + { + sprintf(&ret[idx],"%08lX",(ulong)sk_keyid[1]); + idx+=8; + done=1; + } + break; + + case 'S': /* long key id */ + if(idx+16<maxlen) + { + sprintf(&ret[idx],"%08lX%08lX", + (ulong)sk_keyid[0],(ulong)sk_keyid[1]); + idx+=16; + done=1; + } + break; + + case 'k': /* short key id */ + if(idx+8<maxlen) + { + sprintf(&ret[idx],"%08lX",(ulong)pk_keyid[1]); + idx+=8; + done=1; + } + break; + + case 'K': /* long key id */ + if(idx+16<maxlen) + { + sprintf(&ret[idx],"%08lX%08lX", + (ulong)pk_keyid[0],(ulong)pk_keyid[1]); + idx+=16; + done=1; + } + break; + + case 'f': /* fingerprint */ + { + byte array[MAX_FINGERPRINT_LEN]; + size_t len; + int i; + + if(args->pk) + fingerprint_from_pk(args->pk,array,&len); + else + memset(array,0,MAX_FINGERPRINT_LEN); + + if(idx+(len*2)<maxlen) + { + for(i=0;i<len;i++) + { + sprintf(&ret[idx],"%02X",array[i]); + idx+=2; + } + done=1; + } + } + break; + + case 't': /* e.g. "jpg" */ + str=image_type_to_string(args->imagetype,0); + /* fall through */ + + case 'T': /* e.g. "image/jpeg" */ + if(str==NULL) + str=image_type_to_string(args->imagetype,2); + + if(idx+strlen(str)<maxlen) + { + strcpy(&ret[idx],str); + idx+=strlen(str); + done=1; + } + break; + + case '%': + if(idx+1<maxlen) + { + ret[idx++]='%'; + ret[idx]='\0'; + done=1; + } + break; + + /* Any unknown %-keys (like %i, %o, %I, and %O) are + passed through for later expansion. Note this also + handles the case where the last character in the + string is a '%' - the terminating \0 will end up here + and properly terminate the string. */ + default: + if(idx+2<maxlen) + { + ret[idx++]='%'; + ret[idx++]=*(ch+1); + ret[idx]='\0'; + done=1; + } + break; + } + + if(done) + ch++; + } + else + { + if(idx+1<maxlen) + { + ret[idx++]=*ch; + ret[idx]='\0'; + done=1; + } + } + + if(done) + ch++; + } + + return ret; + + fail: + m_free(ret); + return NULL; } int -pubkey_get_nenc( int algo ) +hextobyte( const char *s ) { - int n = gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NENCR, NULL, 0 ); - return n > 0? n : 0; + int c; + + if( *s >= '0' && *s <= '9' ) + c = 16 * (*s - '0'); + else if( *s >= 'A' && *s <= 'F' ) + c = 16 * (10 + *s - 'A'); + else if( *s >= 'a' && *s <= 'f' ) + c = 16 * (10 + *s - 'a'); + else + return -1; + s++; + if( *s >= '0' && *s <= '9' ) + c += *s - '0'; + else if( *s >= 'A' && *s <= 'F' ) + c += 10 + *s - 'A'; + else if( *s >= 'a' && *s <= 'f' ) + c += 10 + *s - 'a'; + else + return -1; + return c; } - -unsigned int -pubkey_nbits( int algo, MPI *key ) +void +deprecated_warning(const char *configname,unsigned int configlineno, + const char *option,const char *repl1,const char *repl2) { - int rc, nbits; - GCRY_SEXP sexp; + if(configname) + { + if(strncmp("--",option,2)==0) + option+=2; - if( algo == GCRY_PK_DSA ) { - rc = gcry_sexp_build ( &sexp, NULL, - "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))", - key[0], key[1], key[2], key[3] ); - } - else if( algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E ) { - rc = gcry_sexp_build ( &sexp, NULL, - "(public-key(elg(p%m)(g%m)(y%m)))", - key[0], key[1], key[2] ); - } - else if( algo == GCRY_PK_RSA ) { - rc = gcry_sexp_build ( &sexp, NULL, - "(public-key(rsa(n%m)(e%m)))", - key[0], key[1] ); + if(strncmp("--",repl1,2)==0) + repl1+=2; + + log_info(_("%s:%d: deprecated option \"%s\"\n"), + configname,configlineno,option); } - else - return 0; + else + log_info(_("WARNING: \"%s\" is a deprecated option\n"),option); + + log_info(_("please use \"%s%s\" instead\n"),repl1,repl2); +} + +const char * +compress_algo_to_string(int algo) +{ + const char *s="?"; - if ( rc ) - BUG (); + switch(algo) + { + case 0: + s="Uncompressed"; + break; - nbits = gcry_pk_get_nbits( sexp ); - gcry_sexp_release( sexp ); - return nbits; + case 1: + s="ZIP"; + break; + + case 2: + s="ZLIB"; + break; + } + + return s; } +int +check_compress_algo(int algo) +{ + if(algo>=0 && algo<=2) + return 0; + + return G10ERR_COMPR_ALGO; +} |