]>
git.saurik.com Git - apt.git/blob - methods/gpgv.cc
3 #include <apt-pkg/acquire-method.h>
4 #include <apt-pkg/configuration.h>
5 #include <apt-pkg/error.h>
6 #include <apt-pkg/gpgv.h>
7 #include <apt-pkg/strutl.h>
8 #include <apt-pkg/fileutl.h>
33 #define GNUPGPREFIX "[GNUPG:]"
34 #define GNUPGBADSIG "[GNUPG:] BADSIG"
35 #define GNUPGERRSIG "[GNUPG:] ERRSIG"
36 #define GNUPGNOPUBKEY "[GNUPG:] NO_PUBKEY"
37 #define GNUPGVALIDSIG "[GNUPG:] VALIDSIG"
38 #define GNUPGGOODSIG "[GNUPG:] GOODSIG"
39 #define GNUPGKEYEXPIRED "[GNUPG:] KEYEXPIRED"
40 #define GNUPGREVKEYSIG "[GNUPG:] REVKEYSIG"
41 #define GNUPGNODATA "[GNUPG:] NODATA"
52 State
getState () const {
53 if ( state
!= Digest :: State :: Configureable
)
55 std :: string
const digestconfig
= _config
-> Find ( "Debug::Acquire::gpgv::configdigest::truststate" , "trusted" );
56 if ( digestconfig
== "weak" )
58 else if ( digestconfig
== "untrusted" )
59 return State :: Untrusted
;
60 return State :: Trusted
;
64 static constexpr Digest Digests
[] = {
65 { Digest :: State :: Untrusted
, "Invalid digest" },
66 { Digest :: State :: Untrusted
, "MD5" },
67 { Digest :: State :: Weak
, "SHA1" },
68 { Digest :: State :: Weak
, "RIPE-MD/160" },
69 { Digest :: State :: Trusted
, "Reserved digest" },
70 { Digest :: State :: Trusted
, "Reserved digest" },
71 { Digest :: State :: Trusted
, "Reserved digest" },
72 { Digest :: State :: Trusted
, "Reserved digest" },
73 { Digest :: State :: Trusted
, "SHA256" },
74 { Digest :: State :: Trusted
, "SHA384" },
75 { Digest :: State :: Trusted
, "SHA512" },
76 { Digest :: State :: Configureable
, "SHA224" },
78 static_assert ( Digests
[ _count ( Digests
) - 1 ]. state
== Digest :: State :: Configureable
, "the last digest algo isn't the configurable one which we expect for tests" );
80 static Digest
FindDigest ( std :: string
const & Digest
)
82 int id
= atoi ( Digest
. c_str ());
83 if ( id
>= 0 && static_cast < unsigned >( id
) < _count ( Digests
)) {
94 static bool IsTheSameKey ( std :: string
const & validsig
, std :: string
const & goodsig
) {
95 // VALIDSIG reports a keyid (40 = 24 + 16), GOODSIG is a longid (16) only
96 return validsig
. compare ( 24 , 16 , goodsig
, strlen ( "GOODSIG " ), 16 ) == 0 ;
99 class GPGVMethod
: public aptMethod
102 string
VerifyGetSigners ( const char * file
, const char * outfile
,
103 std :: string
const & key
,
104 vector
< string
> & GoodSigners
,
105 vector
< string
> & BadSigners
,
106 vector
< string
> & WorthlessSigners
,
107 vector
< Signer
> & SoonWorthlessSigners
,
108 vector
< string
> & NoPubKeySigners
);
110 virtual bool URIAcquire ( std :: string
const & Message
, FetchItem
* Itm
) APT_OVERRIDE
;
112 GPGVMethod () : aptMethod ( "gpgv" , "1.0" , SingleInstance
| SendConfig
) {};
115 string
GPGVMethod :: VerifyGetSigners ( const char * file
, const char * outfile
,
116 std :: string
const & key
,
117 vector
< string
> & GoodSigners
,
118 vector
< string
> & BadSigners
,
119 vector
< string
> & WorthlessSigners
,
120 vector
< Signer
> & SoonWorthlessSigners
,
121 vector
< string
> & NoPubKeySigners
)
123 bool const Debug
= _config
-> FindB ( "Debug::Acquire::gpgv" , false );
126 std :: clog
<< "inside VerifyGetSigners" << std :: endl
;
129 bool const keyIsID
= ( key
. empty () == false && key
[ 0 ] != '/' );
132 return "Couldn't create pipe" ;
136 return string ( "Couldn't spawn new process" ) + strerror ( errno
);
138 ExecGPGV ( outfile
, file
, 3 , fd
, ( keyIsID
? "" : key
));
141 FILE * pipein
= fdopen ( fd
[ 0 ], "r" );
143 // Loop over the output of apt-key (which really is gnupg), and check the signatures.
144 std :: vector
< std :: string
> ValidSigners
;
145 std :: vector
< std :: string
> ErrSigners
;
146 size_t buffersize
= 0 ;
150 if ( getline (& buffer
, & buffersize
, pipein
) == - 1 )
153 std :: clog
<< "Read: " << buffer
<< std :: endl
;
155 // Push the data into three separate vectors, which
156 // we later concatenate. They're kept separate so
157 // if we improve the apt method communication stuff later
158 // it will be better.
159 if ( strncmp ( buffer
, GNUPGBADSIG
, sizeof ( GNUPGBADSIG
)- 1 ) == 0 )
162 std :: clog
<< "Got BADSIG! " << std :: endl
;
163 BadSigners
. push_back ( string ( buffer
+ sizeof ( GNUPGPREFIX
)));
165 else if ( strncmp ( buffer
, GNUPGERRSIG
, sizeof ( GNUPGERRSIG
)- 1 ) == 0 )
168 std :: clog
<< "Got ERRSIG " << std :: endl
;
169 ErrSigners
. push_back ( string ( buffer
, strlen ( GNUPGPREFIX
), strlen ( "ERRSIG " ) + 16 ));
171 else if ( strncmp ( buffer
, GNUPGNOPUBKEY
, sizeof ( GNUPGNOPUBKEY
)- 1 ) == 0 )
174 std :: clog
<< "Got NO_PUBKEY " << std :: endl
;
175 NoPubKeySigners
. push_back ( string ( buffer
+ sizeof ( GNUPGPREFIX
)));
176 ErrSigners
. erase ( std :: remove_if ( ErrSigners
. begin (), ErrSigners
. end (), [&]( std :: string
const & errsig
) {
177 return errsig
. compare ( strlen ( "ERRSIG " ), 16 , buffer
, strlen ( GNUPGNOPUBKEY
), 16 ) == 0 ; }), ErrSigners
. end ());
179 else if ( strncmp ( buffer
, GNUPGNODATA
, sizeof ( GNUPGBADSIG
)- 1 ) == 0 )
182 std :: clog
<< "Got NODATA! " << std :: endl
;
183 BadSigners
. push_back ( string ( buffer
+ sizeof ( GNUPGPREFIX
)));
185 else if ( strncmp ( buffer
, GNUPGKEYEXPIRED
, sizeof ( GNUPGKEYEXPIRED
)- 1 ) == 0 )
188 std :: clog
<< "Got KEYEXPIRED! " << std :: endl
;
189 WorthlessSigners
. push_back ( string ( buffer
+ sizeof ( GNUPGPREFIX
)));
191 else if ( strncmp ( buffer
, GNUPGREVKEYSIG
, sizeof ( GNUPGREVKEYSIG
)- 1 ) == 0 )
194 std :: clog
<< "Got REVKEYSIG! " << std :: endl
;
195 WorthlessSigners
. push_back ( string ( buffer
+ sizeof ( GNUPGPREFIX
)));
197 else if ( strncmp ( buffer
, GNUPGGOODSIG
, sizeof ( GNUPGGOODSIG
)- 1 ) == 0 )
199 char * sig
= buffer
+ sizeof ( GNUPGPREFIX
);
200 char * p
= sig
+ sizeof ( "GOODSIG" );
201 while (* p
&& isxdigit (* p
))
205 std :: clog
<< "Got GOODSIG, key ID:" << sig
<< std :: endl
;
206 GoodSigners
. push_back ( string ( sig
));
208 else if ( strncmp ( buffer
, GNUPGVALIDSIG
, sizeof ( GNUPGVALIDSIG
)- 1 ) == 0 )
210 char * sig
= buffer
+ sizeof ( GNUPGVALIDSIG
);
211 std :: istringstream
iss (( string ( sig
)));
212 vector
< string
> tokens
{ std :: istream_iterator
< string
>{ iss
},
213 std :: istream_iterator
< string
>{}};
215 while (* p
&& isxdigit (* p
))
218 // Reject weak digest algorithms
219 Digest digest
= FindDigest ( tokens
[ 7 ]);
220 switch ( digest
. getState ()) {
221 case Digest :: State :: Weak
:
222 // Treat them like an expired key: For that a message about expiry
223 // is emitted, a VALIDSIG, but no GOODSIG.
224 SoonWorthlessSigners
. push_back ({ string ( sig
), digest
. name
});
226 std :: clog
<< "Got weak VALIDSIG, key ID: " << sig
<< std :: endl
;
228 case Digest :: State :: Untrusted
:
229 // Treat them like an expired key: For that a message about expiry
230 // is emitted, a VALIDSIG, but no GOODSIG.
231 WorthlessSigners
. push_back ( string ( sig
));
232 GoodSigners
. erase ( std :: remove_if ( GoodSigners
. begin (), GoodSigners
. end (), [&]( std :: string
const & goodsig
) {
233 return IsTheSameKey ( string ( sig
), goodsig
); }), GoodSigners
. end ());
235 std :: clog
<< "Got untrusted VALIDSIG, key ID: " << sig
<< std :: endl
;
237 case Digest :: State :: Configureable
:
238 case Digest :: State :: Trusted
:
240 std :: clog
<< "Got trusted VALIDSIG, key ID: " << sig
<< std :: endl
;
244 ValidSigners
. push_back ( string ( sig
));
249 std :: move ( ErrSigners
. begin (), ErrSigners
. end (), std :: back_inserter ( WorthlessSigners
));
251 // apt-key has a --keyid parameter, but this requires gpg, so we call it without it
252 // and instead check after the fact which keyids where used for verification
256 std :: clog
<< "GoodSigs needs to be limited to keyid " << key
<< std :: endl
;
257 std :: vector
< std :: string
>:: iterator
const foundItr
= std :: find ( ValidSigners
. begin (), ValidSigners
. end (), key
);
258 bool const found
= ( foundItr
!= ValidSigners
. end ());
259 std :: copy ( GoodSigners
. begin (), GoodSigners
. end (), std :: back_insert_iterator
< std :: vector
< std :: string
> >( NoPubKeySigners
));
262 // we look for GOODSIG here as well as an expired sig is a valid sig as well (but not a good one)
263 std :: string
const goodlongkeyid
= "GOODSIG " + key
. substr ( 24 , 16 );
264 bool const foundGood
= std :: find ( GoodSigners
. begin (), GoodSigners
. end (), goodlongkeyid
) != GoodSigners
. end ();
266 std :: clog
<< "Key " << key
<< " is valid sig, is " << goodlongkeyid
<< " also a good one? " << ( foundGood
? "yes" : "no" ) << std :: endl
;
270 GoodSigners
. push_back ( goodlongkeyid
);
271 NoPubKeySigners
. erase ( std :: remove ( NoPubKeySigners
. begin (), NoPubKeySigners
. end (), goodlongkeyid
), NoPubKeySigners
. end ());
279 waitpid ( pid
, & status
, 0 );
282 ioprintf ( std :: clog
, "gpgv exited with status %i \n " , WEXITSTATUS ( status
));
287 std :: cerr
<< "Summary:" << std :: endl
<< " Good: " ;
288 std :: copy ( GoodSigners
. begin (), GoodSigners
. end (), std :: ostream_iterator
< std :: string
>( std :: cerr
, ", " ));
289 std :: cerr
<< std :: endl
<< " Bad: " ;
290 std :: copy ( BadSigners
. begin (), BadSigners
. end (), std :: ostream_iterator
< std :: string
>( std :: cerr
, ", " ));
291 std :: cerr
<< std :: endl
<< " Worthless: " ;
292 std :: copy ( WorthlessSigners
. begin (), WorthlessSigners
. end (), std :: ostream_iterator
< std :: string
>( std :: cerr
, ", " ));
293 std :: cerr
<< std :: endl
<< " SoonWorthless: " ;
294 std :: for_each ( SoonWorthlessSigners
. begin (), SoonWorthlessSigners
. end (), []( Signer
const & sig
) { std :: cerr
<< sig
. key
<< ", " ; });
295 std :: cerr
<< std :: endl
<< " NoPubKey: " ;
296 std :: copy ( NoPubKeySigners
. begin (), NoPubKeySigners
. end (), std :: ostream_iterator
< std :: string
>( std :: cerr
, ", " ));
297 std :: cerr
<< std :: endl
;
300 if ( WEXITSTATUS ( status
) == 0 )
304 // gpgv will report success, but we want to enforce a certain keyring
305 // so if we haven't found the key the valid we found is in fact invalid
306 if ( GoodSigners
. empty ())
307 return _ ( "At least one invalid signature was encountered." );
311 if ( GoodSigners
. empty ())
312 return _ ( "Internal error: Good signature, but could not determine key fingerprint?!" );
316 else if ( WEXITSTATUS ( status
) == 1 )
317 return _ ( "At least one invalid signature was encountered." );
318 else if ( WEXITSTATUS ( status
) == 111 )
319 return _ ( "Could not execute 'apt-key' to verify signature (is gnupg installed?)" );
320 else if ( WEXITSTATUS ( status
) == 112 )
322 // acquire system checks for "NODATA" to generate GPG errors (the others are only warnings)
324 //TRANSLATORS: %s is a single techy word like 'NODATA'
325 strprintf ( errmsg
, _ ( "Clearsigned file isn't valid, got ' %s ' (does the network require authentication?)" ), "NODATA" );
329 return _ ( "Unknown error executing apt-key" );
332 bool GPGVMethod :: URIAcquire ( std :: string
const & Message
, FetchItem
* Itm
)
334 URI
const Get
= Itm
-> Uri
;
335 string
const Path
= Get
. Host
+ Get
. Path
; // To account for relative paths
336 std :: string
const key
= LookupTag ( Message
, "Signed-By" );
337 vector
< string
> GoodSigners
;
338 vector
< string
> BadSigners
;
339 // a worthless signature is a expired or revoked one
340 vector
< string
> WorthlessSigners
;
341 vector
< Signer
> SoonWorthlessSigners
;
342 vector
< string
> NoPubKeySigners
;
345 Res
. Filename
= Itm
-> DestFile
;
348 // Run apt-key on file, extract contents and get the key ID of the signer
349 string msg
= VerifyGetSigners ( Path
. c_str (), Itm
-> DestFile
. c_str (), key
,
350 GoodSigners
, BadSigners
, WorthlessSigners
,
351 SoonWorthlessSigners
, NoPubKeySigners
);
353 // Check if all good signers are soon worthless and warn in that case
354 if ( std :: all_of ( GoodSigners
. begin (), GoodSigners
. end (), [&]( std :: string
const & good
) {
355 return std :: any_of ( SoonWorthlessSigners
. begin (), SoonWorthlessSigners
. end (), [&]( Signer
const & weak
) {
356 return IsTheSameKey ( weak
. key
, good
);
360 for ( auto const & Signer
: SoonWorthlessSigners
)
361 // TRANSLATORS: The second %s is the reason and is untranslated for repository owners.
362 Warning ( _ ( "Signature by key %s uses weak digest algorithm ( %s )" ), Signer
. key
. c_str (), Signer
. note
. c_str ());
365 if ( GoodSigners
. empty () || ! BadSigners
. empty () || ! NoPubKeySigners
. empty ())
368 // In this case, something bad probably happened, so we just go
369 // with what the other method gave us for an error message.
370 if ( BadSigners
. empty () && WorthlessSigners
. empty () && NoPubKeySigners
. empty ())
374 if (! BadSigners
. empty ())
376 errmsg
+= _ ( "The following signatures were invalid: \n " );
377 for ( vector
< string
>:: iterator I
= BadSigners
. begin ();
378 I
!= BadSigners
. end (); ++ I
)
379 errmsg
+= (* I
+ " \n " );
381 if (! WorthlessSigners
. empty ())
383 errmsg
+= _ ( "The following signatures were invalid: \n " );
384 for ( vector
< string
>:: iterator I
= WorthlessSigners
. begin ();
385 I
!= WorthlessSigners
. end (); ++ I
)
386 errmsg
+= (* I
+ " \n " );
388 if (! NoPubKeySigners
. empty ())
390 errmsg
+= _ ( "The following signatures couldn't be verified because the public key is not available: \n " );
391 for ( vector
< string
>:: iterator I
= NoPubKeySigners
. begin ();
392 I
!= NoPubKeySigners
. end (); ++ I
)
393 errmsg
+= (* I
+ " \n " );
396 // this is only fatal if we have no good sigs or if we have at
397 // least one bad signature. good signatures and NoPubKey signatures
398 // happen easily when a file is signed with multiple signatures
399 if ( GoodSigners
. empty () or ! BadSigners
. empty ())
400 return _error
-> Error ( " %s " , errmsg
. c_str ());
403 // Just pass the raw output up, because passing it as a real data
404 // structure is too difficult with the method stuff. We keep it
405 // as three separate vectors for future extensibility.
406 Res
. GPGVOutput
= GoodSigners
;
407 Res
. GPGVOutput
. insert ( Res
. GPGVOutput
. end (), BadSigners
. begin (), BadSigners
. end ());
408 Res
. GPGVOutput
. insert ( Res
. GPGVOutput
. end (), NoPubKeySigners
. begin (), NoPubKeySigners
. end ());
411 if ( _config
-> FindB ( "Debug::Acquire::gpgv" , false ))
413 std :: clog
<< "apt-key succeeded \n " ;
422 setlocale ( LC_ALL
, "" );