/* misc.c - miscellaneous functions * Copyright (C) 1998, 1999, 2000, 2001, 2002, * 2003 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include #include #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 #include #include #endif #ifdef HAVE_SETRLIMIT #include #include #include #endif #include "gpg.h" #include "util.h" #include "main.h" #include "photoid.h" #include "options.h" #include "i18n.h" #define MAX_EXTERN_MPI_BITS 16384 #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 static int setsysinfo(unsigned long op, void *buffer, unsigned long size, int *start, void *arg, unsigned long flag) { return syscall(__NR_osf_setsysinfo, op, buffer, size, start, arg, flag); } void trap_unaligned(void) { unsigned int buf[2]; buf[0] = SSIN_UACPROC; buf[1] = UAC_SIGBUS | UAC_NOPRINT; setsysinfo(SSI_NVPAIRS, buf, 1, 0, 0, 0); } #else void trap_unaligned(void) { /* dummy */ } #endif int disable_core_dumps() { #ifdef HAVE_DOSISH_SYSTEM return 0; #else #ifdef HAVE_SETRLIMIT struct rlimit limit; limit.rlim_cur = 0; limit.rlim_max = 0; if( !setrlimit( RLIMIT_CORE, &limit ) ) return 0; if( errno != EINVAL && errno != ENOSYS ) log_fatal(_("can't disable core dumps: %s\n"), strerror(errno) ); #endif return 1; #endif } u16 checksum_u16( unsigned n ) { u16 a; a = (n >> 8) & 0xff; a += n & 0xff; return a; } u16 checksum( byte *p, unsigned n ) { u16 a; for(a=0; n; n-- ) a += *p++; return a; } u16 checksum_mpi( gcry_mpi_t a ) { int rc; u16 csum; byte *buffer; size_t nbytes; rc = gcry_mpi_print( GCRYMPI_FMT_PGP, NULL, 0, &nbytes, a ); if (rc) BUG (); /* fixme: for numbers not in secure memory we should use a stack * based buffer and only allocate a larger one if mpi_print return * an error */ buffer = gcry_is_secure(a)? gcry_xmalloc_secure(nbytes):gcry_xmalloc(nbytes); rc = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, NULL, a ); if (rc) BUG (); csum = checksum (buffer, nbytes ); xfree (buffer ); return csum; } u32 buffer_to_u32( const byte *buffer ) { unsigned long a; a = *buffer << 24; a |= buffer[1] << 16; a |= buffer[2] << 8; a |= buffer[3]; return a; } static void no_exp_algo(void) { static int did_note = 0; if( !did_note ) { did_note = 1; log_info(_("Experimental algorithms should not be used!\n")); } } void print_pubkey_algo_note( int algo ) { if( algo >= 100 && algo <= 110 ) no_exp_algo(); } void print_cipher_algo_note( int algo ) { if( algo >= 100 && algo <= 110 ) no_exp_algo(); 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 { static int did_note = 0; if( !did_note ) { did_note = 1; log_info(_("this cipher algorithm is deprecated; " "please use a more standard one!\n")); } } } void print_digest_algo_note( int algo ) { if( algo >= 100 && algo <= 110 ) no_exp_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 * openPGP contraints for the algo ID. */ int openpgp_cipher_test_algo( int algo ) { if( algo < 0 || algo > 110 ) return GPG_ERR_CIPHER_ALGO; return gcry_cipher_test_algo (algo); } int openpgp_pk_test_algo( int algo, unsigned int usage_flags ) { size_t value = usage_flags; if (algo == GCRY_PK_ELG_E) algo = GCRY_PK_ELG; #ifdef __GNUC__ #warning need to handle the usage here? #endif if (algo < 0 || algo > 110) return GPG_ERR_PUBKEY_ALGO; return gcry_pk_algo_info (algo, GCRYCTL_TEST_ALGO, NULL, &value); } int openpgp_pk_algo_usage ( int algo ) { int use = 0; /* they are hardwired in gpg 1.0 */ switch ( algo ) { case PUBKEY_ALGO_RSA: use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH; break; case PUBKEY_ALGO_RSA_E: use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_RSA_S: use = PUBKEY_USAGE_SIG; break; case PUBKEY_ALGO_ELGAMAL_E: use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_DSA: use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; break; case PUBKEY_ALGO_ELGAMAL: use = PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH; break; default: break; } return use; } int openpgp_md_test_algo( int algo ) { if( algo < 0 || algo > 110 ) return GPG_ERR_DIGEST_ALGO; return gcry_md_test_algo (algo); } int openpgp_md_map_name (const char *string) { int i = gcry_md_map_name (string); if (!i && (string[0]=='H' || string[0]=='h')) { /* Didn't find it, so try the Hx format */ long val; char *endptr; string++; val=strtol(string,&endptr,10); if (*string!='\0' && *endptr=='\0' && !openpgp_md_test_algo(val)) i = val; } return i < 0 || i > 110? 0 : i; } int openpgp_cipher_map_name (const char *string) { int i = gcry_cipher_map_name (string); if (!i && (string[0]=='S' || string[0]=='s')) { /* Didn't find it, so try the Sx format */ long val; char *endptr; string++; val=strtol(string,&endptr,10); if (*string!='\0' && *endptr=='\0' && !openpgp_cipher_test_algo(val)) i = val; } return i < 0 || i > 110? 0 : i; } int openpgp_pk_map_name (const char *string) { int i = gcry_pk_map_name (string); return i < 0 || i > 110? 0 : i; } #ifdef USE_IDEA /* Special warning for the IDEA cipher */ void idea_cipher_warn(int show) { 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; } } #endif /* 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) { 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); /* This is used so that %k works in photoid command strings in --list-secret-keys (which of course has a sk, but no pk). */ 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= xrealloc(ret,maxlen); } done=0; if(*ch=='%') { switch(*(ch+1)) { case 's': /* short key id */ if(idx+8sk) { if(args->sk->is_primary) fingerprint_from_sk(args->sk,array,&len); else if(args->sk->main_keyid[0] || args->sk->main_keyid[1]) { PKT_public_key *pk= xcalloc(1, sizeof(PKT_public_key)); if(get_pubkey_fast(pk,args->sk->main_keyid)==0) fingerprint_from_pk(pk,array,&len); else memset(array,0,(len=MAX_FINGERPRINT_LEN)); free_public_key(pk); } else memset(array,0,(len=MAX_FINGERPRINT_LEN)); } else if( ch[1]=='f' && args->pk) fingerprint_from_pk(args->pk,array,&len); else if( ch[1]=='g' && args->sk) fingerprint_from_sk(args->sk,array,&len); else memset(array, 0, (len=MAX_FINGERPRINT_LEN)); if(idx+(len*2)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)= '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; } void deprecated_warning(const char *configname,unsigned int configlineno, const char *option,const char *repl1,const char *repl2) { if(configname) { if(strncmp("--",option,2)==0) option+=2; if(strncmp("--",repl1,2)==0) repl1+=2; log_info(_("%s:%d: deprecated option \"%s\"\n"), configname,configlineno,option); } 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="?"; switch(algo) { case 0: s="Uncompressed"; break; case 1: s="ZIP"; break; case 2: s="ZLIB"; break; } return s; } int string_to_compress_algo(const char *string) { if(ascii_strcasecmp(string,"uncompressed")==0) return 0; else if(ascii_strcasecmp(string,"zip")==0) return 1; else if(ascii_strcasecmp(string,"zlib")==0) return 2; else if(ascii_strcasecmp(string,"z0")==0) return 0; else if(ascii_strcasecmp(string,"z1")==0) return 1; else if(ascii_strcasecmp(string,"z2")==0) return 2; else return -1; } int check_compress_algo(int algo) { if(algo>=0 && algo<=2) return 0; return GPG_ERR_COMPR_ALGO; } int default_cipher_algo(void) { if(opt.def_cipher_algo) return opt.def_cipher_algo; else if(opt.personal_cipher_prefs) return opt.personal_cipher_prefs[0].value; else return opt.s2k_cipher_algo; } /* There is no default_digest_algo function, but see sign.c:hash_for */ int default_compress_algo(void) { if(opt.def_compress_algo!=-1) return opt.def_compress_algo; else if(opt.personal_compress_prefs) return opt.personal_compress_prefs[0].value; else return DEFAULT_COMPRESS_ALGO; } const char * compliance_option_string(void) { switch(opt.compliance) { case CO_RFC2440: return "--openpgp"; case CO_PGP2: return "--pgp2"; case CO_PGP6: return "--pgp6"; case CO_PGP7: return "--pgp7"; case CO_PGP8: return "--pgp8"; default: return "???"; } } static const char * compliance_string(void) { switch(opt.compliance) { case CO_RFC2440: return "OpenPGP"; case CO_PGP2: return "PGP 2.x"; case CO_PGP6: return "PGP 6.x"; case CO_PGP7: return "PGP 7.x"; case CO_PGP8: return "PGP 8.x"; default: return "???"; } } void compliance_failure(void) { log_info(_("this message may not be usable by %s\n"),compliance_string()); opt.compliance=CO_GNUPG; } int parse_options(char *str,unsigned int *options,struct parse_options *opts) { char *tok; while((tok=strsep(&str," ,"))) { int i,rev=0; if(tok[0]=='\0') continue; if(ascii_strncasecmp("no-",tok,3)==0) { rev=1; tok+=3; } for(i=0;opts[i].name;i++) { if(ascii_strcasecmp(opts[i].name,tok)==0) { if(rev) *options&=~opts[i].bit; else *options|=opts[i].bit; break; } } if(!opts[i].name) return 0; } return 1; } /* Temporary helper. */ int pubkey_get_npkey( int algo ) { size_t n; if (algo == GCRY_PK_ELG_E) algo = GCRY_PK_ELG; if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &n)) n = 0; return n; } /* Temporary helper. */ int pubkey_get_nskey( int algo ) { size_t n; if (algo == GCRY_PK_ELG_E) algo = GCRY_PK_ELG; if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &n )) n = 0; return n; } /* Temporary helper. */ int pubkey_get_nsig( int algo ) { size_t n; if (algo == GCRY_PK_ELG_E) algo = GCRY_PK_ELG; if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSIGN, NULL, &n)) n = 0; return n; } /* Temporary helper. */ int pubkey_get_nenc( int algo ) { size_t n; if (algo == GCRY_PK_ELG_E) algo = GCRY_PK_ELG; if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NENCR, NULL, &n )) n = 0; return n; } /* Temporary helper. */ unsigned int pubkey_nbits( int algo, gcry_mpi_t *key ) { int rc, nbits; gcry_sexp_t sexp; 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] ); } else return 0; if ( rc ) BUG (); nbits = gcry_pk_get_nbits( sexp ); gcry_sexp_release( sexp ); return nbits; } /* MPI helper functions. */ /**************** * write an mpi to out. */ int mpi_write( iobuf_t out, gcry_mpi_t 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, &nbytes, a ); if( !rc ) rc = iobuf_write( out, buffer, nbytes ); return rc; } /**************** * Writyeg a MPI to out, but in this case it is an opaque one, * s used vor v3 protected keys. */ int mpi_write_opaque( iobuf_t out, gcry_mpi_t 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). */ gcry_mpi_t mpi_read(iobuf_t inp, unsigned int *ret_nread, int secure) { int c, c1, c2, i; unsigned int nbits, nbytes, nread=0; gcry_mpi_t 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, &nread ) ) a = NULL; leave: gcry_free(buf); if( nread > *ret_nread ) log_bug("mpi larger than packet"); else *ret_nread = nread; return 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_t mpi_read_opaque(iobuf_t inp, unsigned *ret_nread ) { int c, c1, c2, i; unsigned nbits, nbytes, nread=0; gcry_mpi_t 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; } 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; return a; } int mpi_print( FILE *fp, gcry_mpi_t 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, &buffer, NULL, a ); assert( !rc ); fputs( buffer, fp ); n += strlen(buffer); gcry_free( buffer ); } return n; }