2002-03-25 23:16:26 +03:00
/** \ingroup signature
* \ file lib / signature . c
*/
/* signature.c - RPM signature functions */
/* NOTES
*
* Things have been cleaned up wrt PGP . We can now handle
* signatures of any length ( which means you can use any
* size key you like ) . We also honor PGPPATH finally .
*/
# include "system.h"
# include "rpmio_internal.h"
2002-03-26 01:02:39 +03:00
# include "rpmlib.h"
# include "rpmmacro.h" /* XXX for rpmGetPath() */
2002-03-25 23:16:26 +03:00
# include "md5.h"
# include "misc.h" /* XXX for dosetenv() and makeTempFile() */
# include "rpmlead.h"
# include "signature.h"
# include "debug.h"
/*@access Header@*/ /* XXX compared with NULL */
/*@access FD_t@*/ /* XXX compared with NULL */
typedef int ( * md5func ) ( const char * fn , /*@out@*/ byte * digest ) ;
int rpmLookupSignatureType ( int action )
{
static int disabled = 0 ;
int rc = 0 ;
switch ( action ) {
case RPMLOOKUPSIG_DISABLE :
disabled = - 2 ;
break ;
case RPMLOOKUPSIG_ENABLE :
disabled = 0 ;
/*@fallthrough@*/
case RPMLOOKUPSIG_QUERY :
if ( disabled )
break ; /* Disabled */
{ const char * name = rpmExpand ( " %{?_signature} " , NULL ) ;
if ( ! ( name & & * name ! = ' \0 ' ) )
rc = 0 ;
else if ( ! xstrcasecmp ( name , " none " ) )
rc = 0 ;
else if ( ! xstrcasecmp ( name , " pgp " ) )
rc = RPMSIGTAG_PGP ;
else if ( ! xstrcasecmp ( name , " pgp5 " ) ) /* XXX legacy */
rc = RPMSIGTAG_PGP ;
else if ( ! xstrcasecmp ( name , " gpg " ) )
rc = RPMSIGTAG_GPG ;
else
rc = - 1 ; /* Invalid %_signature spec in macro file */
name = _free ( name ) ;
} break ;
}
return rc ;
}
/* rpmDetectPGPVersion() returns the absolute path to the "pgp" */
/* executable of the requested version, or NULL when none found. */
const char * rpmDetectPGPVersion ( pgpVersion * pgpVer )
{
/* Actually this should support having more then one pgp version. */
/* At the moment only one version is possible since we only */
/* have one %_pgpbin and one %_pgp_path. */
static pgpVersion saved_pgp_version = PGP_UNKNOWN ;
const char * pgpbin = rpmGetPath ( " %{?_pgpbin} " , NULL ) ;
if ( saved_pgp_version = = PGP_UNKNOWN ) {
char * pgpvbin ;
struct stat st ;
if ( ! ( pgpbin & & pgpbin [ 0 ] ! = ' \0 ' ) ) {
pgpbin = _free ( pgpbin ) ;
saved_pgp_version = - 1 ;
return NULL ;
}
pgpvbin = ( char * ) alloca ( strlen ( pgpbin ) + sizeof ( " v " ) ) ;
( void ) stpcpy ( stpcpy ( pgpvbin , pgpbin ) , " v " ) ;
if ( stat ( pgpvbin , & st ) = = 0 )
saved_pgp_version = PGP_5 ;
else if ( stat ( pgpbin , & st ) = = 0 )
saved_pgp_version = PGP_2 ;
else
saved_pgp_version = PGP_NOTDETECTED ;
}
if ( pgpVer & & pgpbin )
* pgpVer = saved_pgp_version ;
return pgpbin ;
}
/**
* Check package size .
* @ todo rpmio : use fdSize rather than fstat ( 2 ) to get file size .
* @ param fd package file handle
* @ param siglen signature header size
* @ param pad signature padding
* @ param datalen length of header + payload
* @ return rpmRC return code
*/
static inline rpmRC checkSize ( FD_t fd , int siglen , int pad , int datalen )
/*@globals fileSystem @*/
/*@modifies fileSystem @*/
{
struct stat st ;
2002-04-22 21:06:31 +04:00
int delta ;
2002-03-25 23:16:26 +03:00
rpmRC rc ;
if ( fstat ( Fileno ( fd ) , & st ) )
return RPMRC_FAIL ;
if ( ! S_ISREG ( st . st_mode ) ) {
rpmMessage ( RPMMESS_DEBUG ,
_ ( " file is not regular -- skipping size check \n " ) ) ;
return RPMRC_OK ;
}
2002-04-22 21:06:31 +04:00
delta = ( sizeof ( struct rpmlead ) + siglen + pad + datalen ) - st . st_size ;
switch ( delta ) {
case - 32 : /* XXX rpm-4.0 packages */
case 32 : /* XXX Legacy headers have a HEADER_IMAGE tag added. */
case 0 :
rc = RPMRC_OK ;
break ;
default :
rc = RPMRC_BADSIZE ;
break ;
}
2002-03-25 23:16:26 +03:00
2002-04-22 21:06:31 +04:00
rpmMessage ( ( rc = = RPMRC_OK ? RPMMESS_DEBUG : RPMMESS_WARNING ) ,
2002-03-25 23:16:26 +03:00
_ ( " Expected size: %12d = lead(%d)+sigs(%d)+pad(%d)+data(%d) \n " ) ,
( int ) sizeof ( struct rpmlead ) + siglen + pad + datalen ,
( int ) sizeof ( struct rpmlead ) , siglen , pad , datalen ) ;
2002-04-22 21:06:31 +04:00
rpmMessage ( ( rc = = RPMRC_OK ? RPMMESS_DEBUG : RPMMESS_WARNING ) ,
2002-03-25 23:16:26 +03:00
_ ( " Actual size: %12d \n " ) , ( int ) st . st_size ) ;
return rc ;
}
rpmRC rpmReadSignature ( FD_t fd , Header * headerp , sigType sig_type )
{
byte buf [ 2048 ] ;
int sigSize , pad ;
int_32 type , count ;
int_32 * archSize ;
Header h = NULL ;
rpmRC rc = RPMRC_FAIL ; /* assume failure */
if ( headerp )
* headerp = NULL ;
buf [ 0 ] = 0 ;
switch ( sig_type ) {
case RPMSIGTYPE_NONE :
rpmMessage ( RPMMESS_DEBUG , _ ( " No signature \n " ) ) ;
rc = RPMRC_OK ;
break ;
case RPMSIGTYPE_PGP262_1024 :
rpmMessage ( RPMMESS_DEBUG , _ ( " Old PGP signature \n " ) ) ;
/* These are always 256 bytes */
if ( timedRead ( fd , buf , 256 ) ! = 256 )
break ;
h = headerNew ( ) ;
( void ) headerAddEntry ( h , RPMSIGTAG_PGP , RPM_BIN_TYPE , buf , 152 ) ;
rc = RPMRC_OK ;
break ;
case RPMSIGTYPE_MD5 :
case RPMSIGTYPE_MD5_PGP :
rpmError ( RPMERR_BADSIGTYPE ,
_ ( " Old (internal-only) signature! How did you get that!? \n " ) ) ;
break ;
case RPMSIGTYPE_HEADERSIG :
case RPMSIGTYPE_DISABLE :
/* This is a new style signature */
h = headerRead ( fd , HEADER_MAGIC_YES ) ;
if ( h = = NULL )
break ;
rc = RPMRC_OK ;
sigSize = headerSizeof ( h , HEADER_MAGIC_YES ) ;
pad = ( 8 - ( sigSize % 8 ) ) % 8 ; /* 8-byte pad */
if ( sig_type = = RPMSIGTYPE_HEADERSIG ) {
if ( ! headerGetEntry ( h , RPMSIGTAG_SIZE , & type ,
( void * * ) & archSize , & count ) )
break ;
rc = checkSize ( fd , sigSize , pad , * archSize ) ;
}
if ( pad & & timedRead ( fd , buf , pad ) ! = pad )
rc = RPMRC_SHORTREAD ;
break ;
default :
break ;
}
2002-04-22 21:06:31 +04:00
if ( headerp & & rc = = RPMRC_OK )
2002-03-25 23:16:26 +03:00
* headerp = h ;
else if ( h )
h = headerFree ( h ) ;
return rc ;
}
int rpmWriteSignature ( FD_t fd , Header h )
{
static byte buf [ 8 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
int sigSize , pad ;
int rc ;
rc = headerWrite ( fd , h , HEADER_MAGIC_YES ) ;
if ( rc )
return rc ;
sigSize = headerSizeof ( h , HEADER_MAGIC_YES ) ;
pad = ( 8 - ( sigSize % 8 ) ) % 8 ;
if ( pad ) {
if ( Fwrite ( buf , sizeof ( buf [ 0 ] ) , pad , fd ) ! = pad )
rc = 1 ;
}
rpmMessage ( RPMMESS_DEBUG , _ ( " Signature: size(%d)+pad(%d) \n " ) , sigSize , pad ) ;
return rc ;
}
Header rpmNewSignature ( void )
{
Header h = headerNew ( ) ;
return h ;
}
Header rpmFreeSignature ( Header h )
{
return headerFree ( h ) ;
}
static int makePGPSignature ( const char * file , /*@out@*/ void * * sig ,
/*@out@*/ int_32 * size , /*@null@*/ const char * passPhrase )
/*@globals rpmGlobalMacroContext, fileSystem @*/
/*@modifies *sig, *size, rpmGlobalMacroContext, fileSystem @*/
{
char * sigfile = alloca ( 1024 ) ;
int pid , status ;
int inpipe [ 2 ] ;
struct stat st ;
const char * cmd ;
char * const * av ;
int rc ;
( void ) stpcpy ( stpcpy ( sigfile , file ) , " .sig " ) ;
addMacro ( NULL , " __plaintext_filename " , NULL , file , - 1 ) ;
addMacro ( NULL , " __signature_filename " , NULL , sigfile , - 1 ) ;
inpipe [ 0 ] = inpipe [ 1 ] = 0 ;
( void ) pipe ( inpipe ) ;
if ( ! ( pid = fork ( ) ) ) {
const char * pgp_path = rpmExpand ( " %{?_pgp_path} " , NULL ) ;
const char * path ;
pgpVersion pgpVer ;
( void ) close ( STDIN_FILENO ) ;
( void ) dup2 ( inpipe [ 0 ] , 3 ) ;
( void ) close ( inpipe [ 1 ] ) ;
( void ) dosetenv ( " PGPPASSFD " , " 3 " , 1 ) ;
if ( pgp_path & & * pgp_path ! = ' \0 ' )
( void ) dosetenv ( " PGPPATH " , pgp_path , 1 ) ;
/* dosetenv("PGPPASS", passPhrase, 1); */
if ( ( path = rpmDetectPGPVersion ( & pgpVer ) ) ! = NULL ) {
switch ( pgpVer ) {
case PGP_2 :
cmd = rpmExpand ( " %{?__pgp_sign_cmd} " , NULL ) ;
rc = poptParseArgvString ( cmd , NULL , ( const char * * * ) & av ) ;
if ( ! rc )
rc = execve ( av [ 0 ] , av + 1 , environ ) ;
break ;
case PGP_5 :
cmd = rpmExpand ( " %{?__pgp5_sign_cmd} " , NULL ) ;
rc = poptParseArgvString ( cmd , NULL , ( const char * * * ) & av ) ;
if ( ! rc )
rc = execve ( av [ 0 ] , av + 1 , environ ) ;
break ;
case PGP_UNKNOWN :
case PGP_NOTDETECTED :
errno = ENOENT ;
break ;
}
}
rpmError ( RPMERR_EXEC , _ ( " Could not exec %s: %s \n " ) , " pgp " ,
strerror ( errno ) ) ;
_exit ( RPMERR_EXEC ) ;
}
delMacro ( NULL , " __plaintext_filename " ) ;
delMacro ( NULL , " __signature_filename " ) ;
( void ) close ( inpipe [ 0 ] ) ;
if ( passPhrase )
( void ) write ( inpipe [ 1 ] , passPhrase , strlen ( passPhrase ) ) ;
( void ) write ( inpipe [ 1 ] , " \n " , 1 ) ;
( void ) close ( inpipe [ 1 ] ) ;
( void ) waitpid ( pid , & status , 0 ) ;
if ( ! WIFEXITED ( status ) | | WEXITSTATUS ( status ) ) {
rpmError ( RPMERR_SIGGEN , _ ( " pgp failed \n " ) ) ;
return 1 ;
}
if ( stat ( sigfile , & st ) ) {
/* PGP failed to write signature */
if ( sigfile ) ( void ) unlink ( sigfile ) ; /* Just in case */
rpmError ( RPMERR_SIGGEN , _ ( " pgp failed to write signature \n " ) ) ;
return 1 ;
}
* size = st . st_size ;
rpmMessage ( RPMMESS_DEBUG , _ ( " PGP sig size: %d \n " ) , * size ) ;
* sig = xmalloc ( * size ) ;
{ FD_t fd ;
rc = 0 ;
fd = Fopen ( sigfile , " r.fdio " ) ;
if ( fd ! = NULL & & ! Ferror ( fd ) ) {
rc = timedRead ( fd , * sig , * size ) ;
if ( sigfile ) ( void ) unlink ( sigfile ) ;
( void ) Fclose ( fd ) ;
}
if ( rc ! = * size ) {
* sig = _free ( * sig ) ;
rpmError ( RPMERR_SIGGEN , _ ( " unable to read the signature \n " ) ) ;
return 1 ;
}
}
rpmMessage ( RPMMESS_DEBUG , _ ( " Got %d bytes of PGP sig \n " ) , * size ) ;
return 0 ;
}
/* This is an adaptation of the makePGPSignature function to use GPG instead
* of PGP to create signatures . I think I ' ve made all the changes necessary ,
* but this could be a good place to start looking if errors in GPG signature
* creation crop up .
*/
static int makeGPGSignature ( const char * file , /*@out@*/ void * * sig ,
/*@out@*/ int_32 * size , /*@null@*/ const char * passPhrase )
/*@globals rpmGlobalMacroContext, fileSystem @*/
/*@modifies *sig, *size, rpmGlobalMacroContext, fileSystem @*/
{
char * sigfile = alloca ( 1024 ) ;
int pid , status ;
int inpipe [ 2 ] ;
FILE * fpipe ;
struct stat st ;
const char * cmd ;
char * const * av ;
int rc ;
( void ) stpcpy ( stpcpy ( sigfile , file ) , " .sig " ) ;
addMacro ( NULL , " __plaintext_filename " , NULL , file , - 1 ) ;
addMacro ( NULL , " __signature_filename " , NULL , sigfile , - 1 ) ;
inpipe [ 0 ] = inpipe [ 1 ] = 0 ;
( void ) pipe ( inpipe ) ;
if ( ! ( pid = fork ( ) ) ) {
const char * gpg_path = rpmExpand ( " %{?_gpg_path} " , NULL ) ;
( void ) close ( STDIN_FILENO ) ;
( void ) dup2 ( inpipe [ 0 ] , 3 ) ;
( void ) close ( inpipe [ 1 ] ) ;
if ( gpg_path & & * gpg_path ! = ' \0 ' )
( void ) dosetenv ( " GNUPGHOME " , gpg_path , 1 ) ;
cmd = rpmExpand ( " %{?__gpg_sign_cmd} " , NULL ) ;
rc = poptParseArgvString ( cmd , NULL , ( const char * * * ) & av ) ;
if ( ! rc )
rc = execve ( av [ 0 ] , av + 1 , environ ) ;
rpmError ( RPMERR_EXEC , _ ( " Could not exec %s: %s \n " ) , " gpg " ,
strerror ( errno ) ) ;
_exit ( RPMERR_EXEC ) ;
}
delMacro ( NULL , " __plaintext_filename " ) ;
delMacro ( NULL , " __signature_filename " ) ;
fpipe = fdopen ( inpipe [ 1 ] , " w " ) ;
( void ) close ( inpipe [ 0 ] ) ;
if ( fpipe ) {
fprintf ( fpipe , " %s \n " , ( passPhrase ? passPhrase : " " ) ) ;
( void ) fclose ( fpipe ) ;
}
( void ) waitpid ( pid , & status , 0 ) ;
if ( ! WIFEXITED ( status ) | | WEXITSTATUS ( status ) ) {
rpmError ( RPMERR_SIGGEN , _ ( " gpg failed \n " ) ) ;
return 1 ;
}
if ( stat ( sigfile , & st ) ) {
/* GPG failed to write signature */
if ( sigfile ) ( void ) unlink ( sigfile ) ; /* Just in case */
rpmError ( RPMERR_SIGGEN , _ ( " gpg failed to write signature \n " ) ) ;
return 1 ;
}
* size = st . st_size ;
rpmMessage ( RPMMESS_DEBUG , _ ( " GPG sig size: %d \n " ) , * size ) ;
* sig = xmalloc ( * size ) ;
{ FD_t fd ;
int rc = 0 ;
fd = Fopen ( sigfile , " r.fdio " ) ;
if ( fd ! = NULL & & ! Ferror ( fd ) ) {
rc = timedRead ( fd , * sig , * size ) ;
if ( sigfile ) ( void ) unlink ( sigfile ) ;
( void ) Fclose ( fd ) ;
}
if ( rc ! = * size ) {
* sig = _free ( * sig ) ;
rpmError ( RPMERR_SIGGEN , _ ( " unable to read the signature \n " ) ) ;
return 1 ;
}
}
rpmMessage ( RPMMESS_DEBUG , _ ( " Got %d bytes of GPG sig \n " ) , * size ) ;
return 0 ;
}
int rpmAddSignature ( Header h , const char * file , int_32 sigTag ,
const char * passPhrase )
{
struct stat st ;
int_32 size ;
byte buf [ 16 ] ;
void * sig ;
int ret = - 1 ;
switch ( sigTag ) {
case RPMSIGTAG_SIZE :
( void ) stat ( file , & st ) ;
size = st . st_size ;
ret = 0 ;
( void ) headerAddEntry ( h , RPMSIGTAG_SIZE , RPM_INT32_TYPE , & size , 1 ) ;
break ;
case RPMSIGTAG_MD5 :
ret = mdbinfile ( file , buf ) ;
if ( ret = = 0 )
( void ) headerAddEntry ( h , sigTag , RPM_BIN_TYPE , buf , 16 ) ;
break ;
case RPMSIGTAG_PGP5 : /* XXX legacy */
case RPMSIGTAG_PGP :
rpmMessage ( RPMMESS_VERBOSE , _ ( " Generating signature using PGP. \n " ) ) ;
ret = makePGPSignature ( file , & sig , & size , passPhrase ) ;
if ( ret = = 0 )
( void ) headerAddEntry ( h , sigTag , RPM_BIN_TYPE , sig , size ) ;
break ;
case RPMSIGTAG_GPG :
rpmMessage ( RPMMESS_VERBOSE , _ ( " Generating signature using GPG. \n " ) ) ;
ret = makeGPGSignature ( file , & sig , & size , passPhrase ) ;
if ( ret = = 0 )
( void ) headerAddEntry ( h , sigTag , RPM_BIN_TYPE , sig , size ) ;
break ;
}
return ret ;
}
static rpmVerifySignatureReturn
verifySizeSignature ( const char * datafile , int_32 size , /*@out@*/ char * result )
/*@globals fileSystem @*/
/*@modifies *result, fileSystem @*/
{
struct stat st ;
( void ) stat ( datafile , & st ) ;
if ( size ! = st . st_size ) {
sprintf ( result , " Header+Archive size mismatch. \n "
" Expected %d, saw %d. \n " ,
size , ( int ) st . st_size ) ;
return RPMSIG_BAD ;
}
sprintf ( result , " Header+Archive size OK: %d bytes \n " , size ) ;
return RPMSIG_OK ;
}
# define X(_x) (unsigned)((_x) & 0xff)
static rpmVerifySignatureReturn
verifyMD5Signature ( const char * datafile , const byte * sig ,
/*@out@*/ char * result , md5func fn )
/*@globals fileSystem @*/
/*@modifies *result, fileSystem @*/
{
byte md5sum [ 16 ] ;
memset ( md5sum , 0 , sizeof ( md5sum ) ) ;
( void ) fn ( datafile , md5sum ) ;
if ( memcmp ( md5sum , sig , 16 ) ) {
sprintf ( result , " MD5 sum mismatch \n "
" Expected: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x "
" %02x%02x%02x%02x%02x \n "
" Saw : %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x "
" %02x%02x%02x%02x%02x \n " ,
X ( sig [ 0 ] ) , X ( sig [ 1 ] ) , X ( sig [ 2 ] ) , X ( sig [ 3 ] ) ,
X ( sig [ 4 ] ) , X ( sig [ 5 ] ) , X ( sig [ 6 ] ) , X ( sig [ 7 ] ) ,
X ( sig [ 8 ] ) , X ( sig [ 9 ] ) , X ( sig [ 10 ] ) , X ( sig [ 11 ] ) ,
X ( sig [ 12 ] ) , X ( sig [ 13 ] ) , X ( sig [ 14 ] ) , X ( sig [ 15 ] ) ,
X ( md5sum [ 0 ] ) , X ( md5sum [ 1 ] ) , X ( md5sum [ 2 ] ) , X ( md5sum [ 3 ] ) ,
X ( md5sum [ 4 ] ) , X ( md5sum [ 5 ] ) , X ( md5sum [ 6 ] ) , X ( md5sum [ 7 ] ) ,
X ( md5sum [ 8 ] ) , X ( md5sum [ 9 ] ) , X ( md5sum [ 10 ] ) , X ( md5sum [ 11 ] ) ,
X ( md5sum [ 12 ] ) , X ( md5sum [ 13 ] ) , X ( md5sum [ 14 ] ) , X ( md5sum [ 15 ] ) ) ;
return RPMSIG_BAD ;
}
sprintf ( result , " MD5 sum OK: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x "
" %02x%02x%02x%02x%02x \n " ,
X ( md5sum [ 0 ] ) , X ( md5sum [ 1 ] ) , X ( md5sum [ 2 ] ) , X ( md5sum [ 3 ] ) ,
X ( md5sum [ 4 ] ) , X ( md5sum [ 5 ] ) , X ( md5sum [ 6 ] ) , X ( md5sum [ 7 ] ) ,
X ( md5sum [ 8 ] ) , X ( md5sum [ 9 ] ) , X ( md5sum [ 10 ] ) , X ( md5sum [ 11 ] ) ,
X ( md5sum [ 12 ] ) , X ( md5sum [ 13 ] ) , X ( md5sum [ 14 ] ) , X ( md5sum [ 15 ] ) ) ;
return RPMSIG_OK ;
}
static rpmVerifySignatureReturn
verifyPGPSignature ( const char * datafile , const void * sig , int count ,
/*@out@*/ char * result )
/*@globals rpmGlobalMacroContext, fileSystem @*/
/*@modifies *result, rpmGlobalMacroContext, fileSystem @*/
{
int pid , status , outpipe [ 2 ] ;
/*@only@*/ /*@null@*/ const char * sigfile = NULL ;
byte buf [ BUFSIZ ] ;
FILE * file ;
int res = RPMSIG_OK ;
const char * path ;
pgpVersion pgpVer ;
const char * cmd ;
char * const * av ;
int rc ;
/* What version do we have? */
if ( ( path = rpmDetectPGPVersion ( & pgpVer ) ) = = NULL ) {
errno = ENOENT ;
rpmError ( RPMERR_EXEC , ( " Could not exec %s: %s \n " ) , " pgp " ,
strerror ( errno ) ) ;
_exit ( RPMERR_EXEC ) ;
}
/*
* Sad but true : pgp - 5.0 returns exit value of 0 on bad signature .
* Instead we have to use the text output to detect a bad signature .
*/
if ( pgpVer = = PGP_5 )
res = RPMSIG_BAD ;
/* Write out the signature */
# ifdef DYING
{ const char * tmppath = rpmGetPath ( " %{_tmppath} " , NULL ) ;
sigfile = tempnam ( tmppath , " rpmsig " ) ;
tmppath = _free ( tmppath ) ;
}
sfd = Fopen ( sigfile , " w.fdio " ) ;
if ( sfd ! = NULL & & ! Ferror ( sfd ) ) {
( void ) Fwrite ( sig , sizeof ( char ) , count , sfd ) ;
( void ) Fclose ( sfd ) ;
}
# else
{ FD_t sfd ;
if ( ! makeTempFile ( NULL , & sigfile , & sfd ) ) {
( void ) Fwrite ( sig , sizeof ( char ) , count , sfd ) ;
( void ) Fclose ( sfd ) ;
sfd = NULL ;
}
}
# endif
if ( sigfile = = NULL )
return RPMSIG_BAD ;
addMacro ( NULL , " __plaintext_filename " , NULL , datafile , - 1 ) ;
addMacro ( NULL , " __signature_filename " , NULL , sigfile , - 1 ) ;
/* Now run PGP */
outpipe [ 0 ] = outpipe [ 1 ] = 0 ;
( void ) pipe ( outpipe ) ;
if ( ! ( pid = fork ( ) ) ) {
const char * pgp_path = rpmExpand ( " %{?_pgp_path} " , NULL ) ;
( void ) close ( outpipe [ 0 ] ) ;
( void ) close ( STDOUT_FILENO ) ; /* XXX unnecessary */
( void ) dup2 ( outpipe [ 1 ] , STDOUT_FILENO ) ;
if ( pgp_path & & * pgp_path ! = ' \0 ' )
( void ) dosetenv ( " PGPPATH " , pgp_path , 1 ) ;
switch ( pgpVer ) {
case PGP_2 :
cmd = rpmExpand ( " %{?__pgp_verify_cmd} " , NULL ) ;
rc = poptParseArgvString ( cmd , NULL , ( const char * * * ) & av ) ;
if ( ! rc )
rc = execve ( av [ 0 ] , av + 1 , environ ) ;
break ;
case PGP_5 :
/* Some output (in particular "This signature applies to */
/* another message") is _always_ written to stderr; we */
/* want to catch that output, so dup stdout to stderr: */
{ int save_stderr = dup ( 2 ) ;
( void ) dup2 ( 1 , 2 ) ;
cmd = rpmExpand ( " %{?__pgp5_verify_cmd} " , NULL ) ;
rc = poptParseArgvString ( cmd , NULL , ( const char * * * ) & av ) ;
if ( ! rc )
rc = execve ( av [ 0 ] , av + 1 , environ ) ;
/* Restore stderr so we can print the error message below. */
( void ) dup2 ( save_stderr , 2 ) ;
( void ) close ( save_stderr ) ;
} break ;
case PGP_UNKNOWN :
case PGP_NOTDETECTED :
break ;
}
rpmError ( RPMERR_EXEC , _ ( " Could not exec %s: %s \n " ) , " pgp " ,
strerror ( errno ) ) ;
_exit ( RPMERR_EXEC ) ;
}
delMacro ( NULL , " __plaintext_filename " ) ;
delMacro ( NULL , " __signature_filename " ) ;
( void ) close ( outpipe [ 1 ] ) ;
file = fdopen ( outpipe [ 0 ] , " r " ) ;
result [ 0 ] = ' \0 ' ;
if ( file ) {
char * t = result ;
int nb = 8 * BUFSIZ - 1 ;
while ( fgets ( buf , 1024 , file ) ) {
if ( strncmp ( " File ' " , buf , 6 ) & &
strncmp ( " Text is assu " , buf , 12 ) & &
strncmp ( " This signature applies to another message " , buf , 41 ) & &
buf [ 0 ] ! = ' \n ' ) {
nb - = strlen ( buf ) ;
if ( nb > 0 ) t = stpncpy ( t , buf , nb ) ;
}
if ( ! strncmp ( " WARNING: Can't find the right public key " , buf , 40 ) )
res = RPMSIG_NOKEY ;
else if ( ! strncmp ( " Signature by unknown keyid: " , buf , 27 ) )
res = RPMSIG_NOKEY ;
else if ( ! strncmp ( " WARNING: The signing key is not trusted " , buf , 39 ) )
res = RPMSIG_NOTTRUSTED ;
else if ( ! strncmp ( " Good signature " , buf , 14 ) )
res = RPMSIG_OK ;
}
( void ) fclose ( file ) ;
* t = ' \0 ' ;
}
( void ) waitpid ( pid , & status , 0 ) ;
if ( sigfile ) ( void ) unlink ( sigfile ) ;
sigfile = _free ( sigfile ) ;
if ( ! res & & ( ! WIFEXITED ( status ) | | WEXITSTATUS ( status ) ) ) {
res = RPMSIG_BAD ;
}
return res ;
}
2002-03-26 03:56:54 +03:00
static rpmVerifySignatureReturn
do_verifyGPGSignature ( const char * gpghome , const char * sigfile , const char * datafile , /*@out@*/ char * result )
/*@globals rpmGlobalMacroContext, fileSystem @*/
/*@modifies *result, rpmGlobalMacroContext, fileSystem @*/
{
int pid , outpipe [ 2 ] ;
if ( ! sigfile | | ! datafile )
return RPMSIG_BAD ;
/* Now run GPG */
outpipe [ 0 ] = outpipe [ 1 ] = 0 ;
pipe ( outpipe ) ;
pid = fork ( ) ;
if ( pid < 0 )
{
rpmError ( RPMERR_FORK , _ ( " Couldn't fork %s: %s " ) , " gpg " , strerror ( errno ) ) ;
return RPMSIG_BAD ;
} else
{
if ( ! pid )
{
/* child */
const char * cmd ;
char * const * av ;
int rc ;
close ( outpipe [ 0 ] ) ;
/* gpg version 0.9 sends its output to stderr. */
dup2 ( outpipe [ 1 ] , STDERR_FILENO ) ;
addMacro ( NULL , " __plaintext_filename " , NULL , datafile , - 1 ) ;
addMacro ( NULL , " __signature_filename " , NULL , sigfile , - 1 ) ;
if ( rpm_close_all ( ) ) {
perror ( " rpm_close_all " ) ;
_exit ( - 1 ) ;
}
dosetenv ( " LANG " , " C " , 1 ) ;
dosetenv ( " LANGUAGE " , " C " , 1 ) ;
dosetenv ( " LC_ALL " , " C " , 1 ) ;
if ( gpghome & & * gpghome )
dosetenv ( " GNUPGHOME " , gpghome , 1 ) ;
cmd = rpmExpand ( " %{?__gpg_verify_cmd} " , NULL ) ;
rc = poptParseArgvString ( cmd , NULL , ( const char * * * ) & av ) ;
if ( ! rc )
rc = execv ( av [ 0 ] , av + 1 ) ;
rpmError ( RPMERR_EXEC , _ ( " Could not exec %s: %s \n " ) , " gpg " ,
strerror ( errno ) ) ;
_exit ( RPMERR_EXEC ) ;
} else
{
/* parent */
int res = RPMSIG_OK , status ;
FILE * file = fdopen ( outpipe [ 0 ] , " r " ) ;
unsigned char buf [ BUFSIZ ] ;
const char nokey [ ] = " gpg: Can't check signature: public key not found " ;
close ( outpipe [ 1 ] ) ;
result [ 0 ] = ' \0 ' ;
if ( file )
{
while ( fgets ( buf , sizeof ( buf ) , file ) )
{
strcat ( result , buf ) ;
if ( ! xstrncasecmp ( nokey , buf , sizeof ( nokey ) - 1 ) )
res = RPMSIG_NOKEY ;
}
fclose ( file ) ;
}
while ( waitpid ( pid , & status , 0 ) < 0 )
{
if ( EINTR ! = errno )
{
rpmError ( RPMERR_FORK , _ ( " waitpid failure: %s " ) , strerror ( errno ) ) ;
return RPMSIG_BAD ;
}
}
if ( ! res & & ( ! WIFEXITED ( status ) | | WEXITSTATUS ( status ) ) )
res = RPMSIG_BAD ;
return res ;
}
}
}
2002-03-25 23:16:26 +03:00
static rpmVerifySignatureReturn
verifyGPGSignature ( const char * datafile , const void * sig , int count ,
/*@out@*/ char * result )
/*@globals rpmGlobalMacroContext, fileSystem @*/
/*@modifies *result, rpmGlobalMacroContext, fileSystem @*/
{
2003-11-24 21:59:03 +03:00
const char * sigfile = 0 ;
2002-03-26 03:56:54 +03:00
2002-03-25 23:16:26 +03:00
/* Write out the signature */
{ FD_t sfd ;
if ( ! makeTempFile ( NULL , & sigfile , & sfd ) ) {
( void ) Fwrite ( sig , sizeof ( char ) , count , sfd ) ;
( void ) Fclose ( sfd ) ;
sfd = NULL ;
}
}
2002-03-26 03:56:54 +03:00
if ( ! sigfile )
{
rpmError ( RPMERR_SCRIPT , _ ( " Unable to open temp file. " ) ) ;
return RPMSIG_BAD ;
2002-03-25 23:16:26 +03:00
}
2002-03-26 03:56:54 +03:00
/* Now run GPG */
{
const char * gpg_path = rpmExpand ( " %{?_internal_gpg_path} " , NULL ) ;
const char * gpg_home = ( gpg_path & & * gpg_path ) ? gpg_path : 0 ;
rpmVerifySignatureReturn res = gpg_home ?
do_verifyGPGSignature ( gpg_home , sigfile , datafile , result ) : RPMSIG_NOKEY ;
gpg_path = _free ( gpg_path ) ;
if ( RPMSIG_NOKEY = = res )
{
gpg_path = rpmExpand ( " %{?_gpg_path} " , NULL ) ;
gpg_home = ( gpg_path & & * gpg_path ) ? gpg_path : 0 ;
res = do_verifyGPGSignature ( gpg_home , sigfile , datafile , result ) ;
gpg_path = _free ( gpg_path ) ;
2002-03-25 23:16:26 +03:00
}
2002-03-26 03:56:54 +03:00
unlink ( sigfile ) ;
return res ;
2002-03-25 23:16:26 +03:00
}
}
static int checkPassPhrase ( const char * passPhrase , const int sigTag )
/*@globals rpmGlobalMacroContext, fileSystem @*/
/*@modifies rpmGlobalMacroContext, fileSystem @*/
{
int passPhrasePipe [ 2 ] ;
int pid , status ;
int fd ;
const char * cmd ;
char * const * av ;
int rc ;
passPhrasePipe [ 0 ] = passPhrasePipe [ 1 ] = 0 ;
( void ) pipe ( passPhrasePipe ) ;
if ( ! ( pid = fork ( ) ) ) {
( void ) close ( STDIN_FILENO ) ;
( void ) close ( STDOUT_FILENO ) ;
( void ) close ( passPhrasePipe [ 1 ] ) ;
if ( ! rpmIsVerbose ( ) ) {
( void ) close ( STDERR_FILENO ) ;
}
if ( ( fd = open ( " /dev/null " , O_RDONLY ) ) ! = STDIN_FILENO ) {
( void ) dup2 ( fd , STDIN_FILENO ) ;
( void ) close ( fd ) ;
}
if ( ( fd = open ( " /dev/null " , O_WRONLY ) ) ! = STDOUT_FILENO ) {
( void ) dup2 ( fd , STDOUT_FILENO ) ;
( void ) close ( fd ) ;
}
( void ) dup2 ( passPhrasePipe [ 0 ] , 3 ) ;
switch ( sigTag ) {
case RPMSIGTAG_GPG :
{ const char * gpg_path = rpmExpand ( " %{?_gpg_path} " , NULL ) ;
if ( gpg_path & & * gpg_path ! = ' \0 ' )
( void ) dosetenv ( " GNUPGHOME " , gpg_path , 1 ) ;
cmd = rpmExpand ( " %{?__gpg_check_password_cmd} " , NULL ) ;
rc = poptParseArgvString ( cmd , NULL , ( const char * * * ) & av ) ;
if ( ! rc )
rc = execve ( av [ 0 ] , av + 1 , environ ) ;
rpmError ( RPMERR_EXEC , _ ( " Could not exec %s: %s \n " ) , " gpg " ,
strerror ( errno ) ) ;
_exit ( RPMERR_EXEC ) ;
} /*@notreached@*/ break ;
case RPMSIGTAG_PGP5 : /* XXX legacy */
case RPMSIGTAG_PGP :
{ const char * pgp_path = rpmExpand ( " %{?_pgp_path} " , NULL ) ;
const char * path ;
pgpVersion pgpVer ;
( void ) dosetenv ( " PGPPASSFD " , " 3 " , 1 ) ;
if ( pgp_path & & * pgp_path ! = ' \0 ' )
( void ) dosetenv ( " PGPPATH " , pgp_path , 1 ) ;
if ( ( path = rpmDetectPGPVersion ( & pgpVer ) ) ! = NULL ) {
switch ( pgpVer ) {
case PGP_2 :
cmd = rpmExpand ( " %{?__pgp_check_password_cmd} " , NULL ) ;
rc = poptParseArgvString ( cmd , NULL , ( const char * * * ) & av ) ;
if ( ! rc )
rc = execve ( av [ 0 ] , av + 1 , environ ) ;
break ;
case PGP_5 : /* XXX legacy */
cmd = rpmExpand ( " %{?__pgp5_check_password_cmd} " , NULL ) ;
rc = poptParseArgvString ( cmd , NULL , ( const char * * * ) & av ) ;
if ( ! rc )
rc = execve ( av [ 0 ] , av + 1 , environ ) ;
break ;
case PGP_UNKNOWN :
case PGP_NOTDETECTED :
break ;
}
}
rpmError ( RPMERR_EXEC , _ ( " Could not exec %s: %s \n " ) , " pgp " ,
strerror ( errno ) ) ;
_exit ( RPMERR_EXEC ) ;
} /*@notreached@*/ break ;
default : /* This case should have been screened out long ago. */
rpmError ( RPMERR_SIGGEN , _ ( " Invalid %%_signature spec in macro file \n " ) ) ;
_exit ( RPMERR_SIGGEN ) ;
/*@notreached@*/ break ;
}
}
( void ) close ( passPhrasePipe [ 0 ] ) ;
( void ) write ( passPhrasePipe [ 1 ] , passPhrase , strlen ( passPhrase ) ) ;
( void ) write ( passPhrasePipe [ 1 ] , " \n " , 1 ) ;
( void ) close ( passPhrasePipe [ 1 ] ) ;
( void ) waitpid ( pid , & status , 0 ) ;
if ( ! WIFEXITED ( status ) | | WEXITSTATUS ( status ) ) {
return 1 ;
}
/* passPhrase is good */
return 0 ;
}
char * rpmGetPassPhrase ( const char * prompt , const int sigTag )
{
char * pass ;
int aok ;
switch ( sigTag ) {
case RPMSIGTAG_GPG :
{ const char * name = rpmExpand ( " %{?_gpg_name} " , NULL ) ;
aok = ( name & & * name ! = ' \0 ' ) ;
name = _free ( name ) ;
}
if ( ! aok ) {
rpmError ( RPMERR_SIGGEN ,
_ ( " You must set \" %%_gpg_name \" in your macro file \n " ) ) ;
return NULL ;
}
break ;
case RPMSIGTAG_PGP5 : /* XXX legacy */
case RPMSIGTAG_PGP :
{ const char * name = rpmExpand ( " %{?_pgp_name} " , NULL ) ;
aok = ( name & & * name ! = ' \0 ' ) ;
name = _free ( name ) ;
}
if ( ! aok ) {
rpmError ( RPMERR_SIGGEN ,
_ ( " You must set \" %%_pgp_name \" in your macro file \n " ) ) ;
return NULL ;
}
break ;
default :
/* Currently the calling function (rpm.c:main) is checking this and
* doing a better job . This section should never be accessed .
*/
rpmError ( RPMERR_SIGGEN , _ ( " Invalid %%_signature spec in macro file \n " ) ) ;
return NULL ;
/*@notreached@*/ break ;
}
pass = /*@-unrecog@*/ getpass ( ( prompt ? prompt : " " ) ) /*@=unrecog@*/ ;
if ( checkPassPhrase ( pass , sigTag ) )
return NULL ;
return pass ;
}
rpmVerifySignatureReturn
rpmVerifySignature ( const char * file , int_32 sigTag , const void * sig ,
int count , char * result )
{
rpmVerifySignatureReturn res ;
switch ( sigTag ) {
case RPMSIGTAG_SIZE :
res = verifySizeSignature ( file , * ( int_32 * ) sig , result ) ;
break ;
case RPMSIGTAG_MD5 :
res = verifyMD5Signature ( file , sig , result , mdbinfile ) ;
break ;
case RPMSIGTAG_PGP5 : /* XXX legacy */
case RPMSIGTAG_PGP :
res = verifyPGPSignature ( file , sig , count , result ) ;
break ;
case RPMSIGTAG_GPG :
res = verifyGPGSignature ( file , sig , count , result ) ;
break ;
case RPMSIGTAG_LEMD5_1 :
case RPMSIGTAG_LEMD5_2 :
sprintf ( result , _ ( " Broken MD5 digest: UNSUPPORTED \n " ) ) ;
res = RPMSIG_UNKNOWN ;
break ;
default :
sprintf ( result , " Do not know how to verify sig type %d \n " , sigTag ) ;
res = RPMSIG_UNKNOWN ;
break ;
}
return res ;
}