]> git.saurik.com Git - apt.git/blame - methods/gpgv.cc
refactor loading of previous release file
[apt.git] / methods / gpgv.cc
CommitLineData
ea542140
DK
1#include <config.h>
2
b3d44315 3#include <apt-pkg/acquire-method.h>
472ff00e 4#include <apt-pkg/configuration.h>
453b82a3 5#include <apt-pkg/error.h>
2f5b6151 6#include <apt-pkg/gpgv.h>
453b82a3 7#include <apt-pkg/strutl.h>
3927c6da 8#include <apt-pkg/fileutl.h>
23e64f6d 9#include "aptmethod.h"
b3d44315 10
453b82a3 11#include <ctype.h>
b3d44315 12#include <errno.h>
453b82a3
DK
13#include <stddef.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
b3d44315 17#include <sys/wait.h>
453b82a3 18#include <unistd.h>
4e03c47d 19
d9105124 20#include <array>
4e03c47d 21#include <algorithm>
d9105124
JAK
22#include <sstream>
23#include <iterator>
b3d44315 24#include <iostream>
453b82a3 25#include <string>
f5a3d009
DK
26#include <vector>
27
ea542140
DK
28#include <apti18n.h>
29
8f3ba4e8
DK
30using std::string;
31using std::vector;
32
b3d44315
MV
33#define GNUPGPREFIX "[GNUPG:]"
34#define GNUPGBADSIG "[GNUPG:] BADSIG"
35#define GNUPGNOPUBKEY "[GNUPG:] NO_PUBKEY"
36#define GNUPGVALIDSIG "[GNUPG:] VALIDSIG"
c5d8878d
MV
37#define GNUPGGOODSIG "[GNUPG:] GOODSIG"
38#define GNUPGKEYEXPIRED "[GNUPG:] KEYEXPIRED"
39#define GNUPGREVKEYSIG "[GNUPG:] REVKEYSIG"
2abb68b7 40#define GNUPGNODATA "[GNUPG:] NODATA"
b3d44315 41
349c5c89
JAK
42struct Digest {
43 enum class State {
44 Untrusted,
45 Weak,
46 Trusted,
47 } state;
48 char name[32];
d9105124
JAK
49};
50
349c5c89
JAK
51static constexpr Digest Digests[] = {
52 {Digest::State::Untrusted, "Invalid digest"},
53 {Digest::State::Untrusted, "MD5"},
54 {Digest::State::Weak, "SHA1"},
55 {Digest::State::Weak, "RIPE-MD/160"},
56 {Digest::State::Trusted, "Reserved digest"},
57 {Digest::State::Trusted, "Reserved digest"},
58 {Digest::State::Trusted, "Reserved digest"},
59 {Digest::State::Trusted, "Reserved digest"},
60 {Digest::State::Trusted, "SHA256"},
61 {Digest::State::Trusted, "SHA384"},
62 {Digest::State::Trusted, "SHA512"},
63 {Digest::State::Trusted, "SHA224"},
64};
65
66static Digest FindDigest(std::string const & Digest)
67{
68 int id = atoi(Digest.c_str());
69 if (id >= 0 && static_cast<unsigned>(id) < _count(Digests)) {
70 return Digests[id];
71 } else {
72 return Digests[0];
73 }
74}
75
76struct Signer {
77 std::string key;
78 std::string note;
07ea3af0
JAK
79};
80
23e64f6d 81class GPGVMethod : public aptMethod
b3d44315
MV
82{
83 private:
da9ed163 84 string VerifyGetSigners(const char *file, const char *outfile,
b0d40854
DK
85 std::string const &key,
86 vector<string> &GoodSigners,
c5d8878d
MV
87 vector<string> &BadSigners,
88 vector<string> &WorthlessSigners,
349c5c89 89 vector<Signer> &SoonWorthlessSigners,
b3d44315 90 vector<string> &NoPubKeySigners);
b3d44315 91 protected:
3b302846 92 virtual bool URIAcquire(std::string const &Message, FetchItem *Itm) APT_OVERRIDE;
b3d44315
MV
93 public:
94
23e64f6d 95 GPGVMethod() : aptMethod("gpgv","1.0",SingleInstance | SendConfig) {};
b3d44315
MV
96};
97
da9ed163 98string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
b0d40854 99 std::string const &key,
b3d44315
MV
100 vector<string> &GoodSigners,
101 vector<string> &BadSigners,
c5d8878d 102 vector<string> &WorthlessSigners,
349c5c89 103 vector<Signer> &SoonWorthlessSigners,
b3d44315
MV
104 vector<string> &NoPubKeySigners)
105{
46e39c8e 106 bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
da9ed163 107
46e39c8e
MV
108 if (Debug == true)
109 std::clog << "inside VerifyGetSigners" << std::endl;
110
b3d44315 111 int fd[2];
4e03c47d 112 bool const keyIsID = (key.empty() == false && key[0] != '/');
46e39c8e 113
b3d44315 114 if (pipe(fd) < 0)
b3d44315 115 return "Couldn't create pipe";
b3d44315 116
cf440fac 117 pid_t pid = fork();
b3d44315 118 if (pid < 0)
da9ed163 119 return string("Couldn't spawn new process") + strerror(errno);
b3d44315 120 else if (pid == 0)
4e03c47d 121 ExecGPGV(outfile, file, 3, fd, (keyIsID ? "" : key));
b3d44315
MV
122 close(fd[1]);
123
cf440fac
DK
124 FILE *pipein = fdopen(fd[0], "r");
125
b39bb552 126 // Loop over the output of apt-key (which really is gnupg), and check the signatures.
4e03c47d 127 std::vector<std::string> ValidSigners;
bf6ac7ca
DK
128 size_t buffersize = 0;
129 char *buffer = NULL;
b3d44315
MV
130 while (1)
131 {
bf6ac7ca
DK
132 if (getline(&buffer, &buffersize, pipein) == -1)
133 break;
46e39c8e
MV
134 if (Debug == true)
135 std::clog << "Read: " << buffer << std::endl;
b3d44315
MV
136
137 // Push the data into three separate vectors, which
138 // we later concatenate. They're kept separate so
139 // if we improve the apt method communication stuff later
140 // it will be better.
141 if (strncmp(buffer, GNUPGBADSIG, sizeof(GNUPGBADSIG)-1) == 0)
142 {
46e39c8e
MV
143 if (Debug == true)
144 std::clog << "Got BADSIG! " << std::endl;
b3d44315
MV
145 BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
146 }
4e03c47d 147 else if (strncmp(buffer, GNUPGNOPUBKEY, sizeof(GNUPGNOPUBKEY)-1) == 0)
b3d44315 148 {
46e39c8e
MV
149 if (Debug == true)
150 std::clog << "Got NO_PUBKEY " << std::endl;
b3d44315
MV
151 NoPubKeySigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
152 }
4e03c47d 153 else if (strncmp(buffer, GNUPGNODATA, sizeof(GNUPGBADSIG)-1) == 0)
2abb68b7 154 {
46e39c8e
MV
155 if (Debug == true)
156 std::clog << "Got NODATA! " << std::endl;
2abb68b7
MV
157 BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
158 }
4e03c47d 159 else if (strncmp(buffer, GNUPGKEYEXPIRED, sizeof(GNUPGKEYEXPIRED)-1) == 0)
c5d8878d 160 {
46e39c8e
MV
161 if (Debug == true)
162 std::clog << "Got KEYEXPIRED! " << std::endl;
c5d8878d
MV
163 WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
164 }
4e03c47d 165 else if (strncmp(buffer, GNUPGREVKEYSIG, sizeof(GNUPGREVKEYSIG)-1) == 0)
c5d8878d 166 {
46e39c8e
MV
167 if (Debug == true)
168 std::clog << "Got REVKEYSIG! " << std::endl;
c5d8878d
MV
169 WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
170 }
4e03c47d 171 else if (strncmp(buffer, GNUPGGOODSIG, sizeof(GNUPGGOODSIG)-1) == 0)
b3d44315
MV
172 {
173 char *sig = buffer + sizeof(GNUPGPREFIX);
c5d8878d 174 char *p = sig + sizeof("GOODSIG");
b3d44315
MV
175 while (*p && isxdigit(*p))
176 p++;
177 *p = 0;
46e39c8e
MV
178 if (Debug == true)
179 std::clog << "Got GOODSIG, key ID:" << sig << std::endl;
b3d44315
MV
180 GoodSigners.push_back(string(sig));
181 }
4e03c47d
DK
182 else if (strncmp(buffer, GNUPGVALIDSIG, sizeof(GNUPGVALIDSIG)-1) == 0)
183 {
184 char *sig = buffer + sizeof(GNUPGVALIDSIG);
d9105124
JAK
185 std::istringstream iss((string(sig)));
186 vector<string> tokens{std::istream_iterator<string>{iss},
187 std::istream_iterator<string>{}};
4e03c47d
DK
188 char *p = sig;
189 while (*p && isxdigit(*p))
190 p++;
191 *p = 0;
192 if (Debug == true)
193 std::clog << "Got VALIDSIG, key ID: " << sig << std::endl;
d9105124 194 // Reject weak digest algorithms
349c5c89
JAK
195 Digest digest = FindDigest(tokens[7]);
196 switch (digest.state) {
197 case Digest::State::Weak:
07ea3af0
JAK
198 // Treat them like an expired key: For that a message about expiry
199 // is emitted, a VALIDSIG, but no GOODSIG.
349c5c89
JAK
200 SoonWorthlessSigners.push_back({string(sig), digest.name});
201 break;
202 case Digest::State::Untrusted:
08fd77e8
JAK
203 // Treat them like an expired key: For that a message about expiry
204 // is emitted, a VALIDSIG, but no GOODSIG.
349c5c89 205 WorthlessSigners.push_back(string(sig));
08fd77e8 206 GoodSigners.erase(std::remove(GoodSigners.begin(), GoodSigners.end(), string(sig)));
349c5c89
JAK
207 break;
208 case Digest::State::Trusted:
209 break;
08fd77e8 210 }
d9105124 211
4e03c47d
DK
212 ValidSigners.push_back(string(sig));
213 }
b3d44315
MV
214 }
215 fclose(pipein);
1b7bf822 216 free(buffer);
b3d44315 217
4e03c47d
DK
218 // apt-key has a --keyid parameter, but this requires gpg, so we call it without it
219 // and instead check after the fact which keyids where used for verification
220 if (keyIsID == true)
221 {
222 if (Debug == true)
223 std::clog << "GoodSigs needs to be limited to keyid " << key << std::endl;
224 std::vector<std::string>::iterator const foundItr = std::find(ValidSigners.begin(), ValidSigners.end(), key);
225 bool const found = (foundItr != ValidSigners.end());
226 std::copy(GoodSigners.begin(), GoodSigners.end(), std::back_insert_iterator<std::vector<std::string> >(NoPubKeySigners));
227 if (found)
228 {
229 // we look for GOODSIG here as well as an expired sig is a valid sig as well (but not a good one)
230 std::string const goodlongkeyid = "GOODSIG " + key.substr(24, 16);
231 bool const foundGood = std::find(GoodSigners.begin(), GoodSigners.end(), goodlongkeyid) != GoodSigners.end();
232 if (Debug == true)
233 std::clog << "Key " << key << " is valid sig, is " << goodlongkeyid << " also a good one? " << (foundGood ? "yes" : "no") << std::endl;
234 GoodSigners.clear();
235 if (foundGood)
236 {
237 GoodSigners.push_back(goodlongkeyid);
238 NoPubKeySigners.erase(std::remove(NoPubKeySigners.begin(), NoPubKeySigners.end(), goodlongkeyid), NoPubKeySigners.end());
239 }
240 }
241 else
242 GoodSigners.clear();
243 }
244
cf440fac 245 int status;
b3d44315 246 waitpid(pid, &status, 0);
46e39c8e 247 if (Debug == true)
b3d44315 248 {
2737f28a 249 ioprintf(std::clog, "gpgv exited with status %i\n", WEXITSTATUS(status));
b3d44315
MV
250 }
251
252 if (WEXITSTATUS(status) == 0)
253 {
4e03c47d
DK
254 if (keyIsID)
255 {
256 // gpgv will report success, but we want to enforce a certain keyring
257 // so if we haven't found the key the valid we found is in fact invalid
258 if (GoodSigners.empty())
259 return _("At least one invalid signature was encountered.");
260 }
261 else
262 {
263 if (GoodSigners.empty())
264 return _("Internal error: Good signature, but could not determine key fingerprint?!");
265 }
da9ed163 266 return "";
b3d44315
MV
267 }
268 else if (WEXITSTATUS(status) == 1)
339690e4 269 return _("At least one invalid signature was encountered.");
b3d44315 270 else if (WEXITSTATUS(status) == 111)
b39bb552 271 return _("Could not execute 'apt-key' to verify signature (is gnupg installed?)");
ae99ce2e 272 else if (WEXITSTATUS(status) == 112)
b3d44315 273 {
ae99ce2e
DK
274 // acquire system checks for "NODATA" to generate GPG errors (the others are only warnings)
275 std::string errmsg;
276 //TRANSLATORS: %s is a single techy word like 'NODATA'
277 strprintf(errmsg, _("Clearsigned file isn't valid, got '%s' (does the network require authentication?)"), "NODATA");
278 return errmsg;
b3d44315
MV
279 }
280 else
b39bb552 281 return _("Unknown error executing apt-key");
b3d44315
MV
282}
283
b0d40854 284bool GPGVMethod::URIAcquire(std::string const &Message, FetchItem *Itm)
b3d44315 285{
b0d40854
DK
286 URI const Get = Itm->Uri;
287 string const Path = Get.Host + Get.Path; // To account for relative paths
288 std::string const key = LookupTag(Message, "Signed-By");
b3d44315
MV
289 vector<string> GoodSigners;
290 vector<string> BadSigners;
c5d8878d
MV
291 // a worthless signature is a expired or revoked one
292 vector<string> WorthlessSigners;
349c5c89 293 vector<Signer> SoonWorthlessSigners;
b3d44315
MV
294 vector<string> NoPubKeySigners;
295
296 FetchResult Res;
297 Res.Filename = Itm->DestFile;
298 URIStart(Res);
299
b39bb552 300 // Run apt-key on file, extract contents and get the key ID of the signer
b0d40854 301 string msg = VerifyGetSigners(Path.c_str(), Itm->DestFile.c_str(), key,
c5d8878d 302 GoodSigners, BadSigners, WorthlessSigners,
07ea3af0
JAK
303 SoonWorthlessSigners, NoPubKeySigners);
304
305
306 // Check if there are any good signers that are not soon worthless
307 std::vector<std::string> NotWarnAboutSigners(GoodSigners);
308 for (auto const & Signer : SoonWorthlessSigners)
349c5c89 309 NotWarnAboutSigners.erase(std::remove(NotWarnAboutSigners.begin(), NotWarnAboutSigners.end(), "GOODSIG " + Signer.key));
07ea3af0
JAK
310 // If all signers are soon worthless, report them.
311 if (NotWarnAboutSigners.empty()) {
312 for (auto const & Signer : SoonWorthlessSigners)
313 // TRANSLATORS: The second %s is the reason and is untranslated for repository owners.
5f060c27 314 Warning(_("Signature by key %s uses weak digest algorithm (%s)"), Signer.key.c_str(), Signer.note.c_str());
07ea3af0
JAK
315 }
316
b3d44315
MV
317 if (GoodSigners.empty() || !BadSigners.empty() || !NoPubKeySigners.empty())
318 {
319 string errmsg;
320 // In this case, something bad probably happened, so we just go
321 // with what the other method gave us for an error message.
c5d8878d 322 if (BadSigners.empty() && WorthlessSigners.empty() && NoPubKeySigners.empty())
b3d44315
MV
323 errmsg = msg;
324 else
325 {
326 if (!BadSigners.empty())
327 {
339690e4 328 errmsg += _("The following signatures were invalid:\n");
b3d44315 329 for (vector<string>::iterator I = BadSigners.begin();
f7f0d6c7 330 I != BadSigners.end(); ++I)
b3d44315
MV
331 errmsg += (*I + "\n");
332 }
c5d8878d
MV
333 if (!WorthlessSigners.empty())
334 {
335 errmsg += _("The following signatures were invalid:\n");
336 for (vector<string>::iterator I = WorthlessSigners.begin();
f7f0d6c7 337 I != WorthlessSigners.end(); ++I)
c5d8878d
MV
338 errmsg += (*I + "\n");
339 }
b3d44315
MV
340 if (!NoPubKeySigners.empty())
341 {
339690e4 342 errmsg += _("The following signatures couldn't be verified because the public key is not available:\n");
b3d44315 343 for (vector<string>::iterator I = NoPubKeySigners.begin();
f7f0d6c7 344 I != NoPubKeySigners.end(); ++I)
b3d44315
MV
345 errmsg += (*I + "\n");
346 }
347 }
ce424cd4
MV
348 // this is only fatal if we have no good sigs or if we have at
349 // least one bad signature. good signatures and NoPubKey signatures
350 // happen easily when a file is signed with multiple signatures
351 if(GoodSigners.empty() or !BadSigners.empty())
f23153d0 352 return _error->Error("%s", errmsg.c_str());
b3d44315
MV
353 }
354
b3d44315
MV
355 // Just pass the raw output up, because passing it as a real data
356 // structure is too difficult with the method stuff. We keep it
357 // as three separate vectors for future extensibility.
358 Res.GPGVOutput = GoodSigners;
359 Res.GPGVOutput.insert(Res.GPGVOutput.end(),BadSigners.begin(),BadSigners.end());
360 Res.GPGVOutput.insert(Res.GPGVOutput.end(),NoPubKeySigners.begin(),NoPubKeySigners.end());
361 URIDone(Res);
362
363 if (_config->FindB("Debug::Acquire::gpgv", false))
364 {
b39bb552 365 std::clog << "apt-key succeeded\n";
b3d44315
MV
366 }
367
368 return true;
369}
370
371
372int main()
373{
339690e4 374 setlocale(LC_ALL, "");
3927c6da 375
b3d44315
MV
376 GPGVMethod Mth;
377
378 return Mth.Run();
379}