]> git.saurik.com Git - apt.git/blame - methods/gpgv.cc
Handle ERRSIG in the gpgv method like BADSIG
[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
DK
19
20#include <algorithm>
b3d44315 21#include <iostream>
453b82a3 22#include <string>
f5a3d009
DK
23#include <vector>
24
ea542140
DK
25#include <apti18n.h>
26
8f3ba4e8
DK
27using std::string;
28using std::vector;
29
b3d44315
MV
30#define GNUPGPREFIX "[GNUPG:]"
31#define GNUPGBADSIG "[GNUPG:] BADSIG"
76a71a12 32#define GNUPGERRSIG "[GNUPG:] ERRSIG"
b3d44315
MV
33#define GNUPGNOPUBKEY "[GNUPG:] NO_PUBKEY"
34#define GNUPGVALIDSIG "[GNUPG:] VALIDSIG"
c5d8878d
MV
35#define GNUPGGOODSIG "[GNUPG:] GOODSIG"
36#define GNUPGKEYEXPIRED "[GNUPG:] KEYEXPIRED"
37#define GNUPGREVKEYSIG "[GNUPG:] REVKEYSIG"
2abb68b7 38#define GNUPGNODATA "[GNUPG:] NODATA"
b3d44315 39
23e64f6d 40class GPGVMethod : public aptMethod
b3d44315
MV
41{
42 private:
da9ed163 43 string VerifyGetSigners(const char *file, const char *outfile,
b0d40854
DK
44 std::string const &key,
45 vector<string> &GoodSigners,
c5d8878d
MV
46 vector<string> &BadSigners,
47 vector<string> &WorthlessSigners,
b3d44315
MV
48 vector<string> &NoPubKeySigners);
49
50 protected:
3b302846 51 virtual bool URIAcquire(std::string const &Message, FetchItem *Itm) APT_OVERRIDE;
b3d44315
MV
52 public:
53
23e64f6d 54 GPGVMethod() : aptMethod("gpgv","1.0",SingleInstance | SendConfig) {};
b3d44315
MV
55};
56
da9ed163 57string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
b0d40854 58 std::string const &key,
b3d44315
MV
59 vector<string> &GoodSigners,
60 vector<string> &BadSigners,
c5d8878d 61 vector<string> &WorthlessSigners,
b3d44315
MV
62 vector<string> &NoPubKeySigners)
63{
46e39c8e 64 bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
da9ed163 65
46e39c8e
MV
66 if (Debug == true)
67 std::clog << "inside VerifyGetSigners" << std::endl;
68
b3d44315 69 int fd[2];
4e03c47d 70 bool const keyIsID = (key.empty() == false && key[0] != '/');
46e39c8e 71
b3d44315 72 if (pipe(fd) < 0)
b3d44315 73 return "Couldn't create pipe";
b3d44315 74
cf440fac 75 pid_t pid = fork();
b3d44315 76 if (pid < 0)
da9ed163 77 return string("Couldn't spawn new process") + strerror(errno);
b3d44315 78 else if (pid == 0)
4e03c47d 79 ExecGPGV(outfile, file, 3, fd, (keyIsID ? "" : key));
b3d44315
MV
80 close(fd[1]);
81
cf440fac
DK
82 FILE *pipein = fdopen(fd[0], "r");
83
b39bb552 84 // Loop over the output of apt-key (which really is gnupg), and check the signatures.
4e03c47d 85 std::vector<std::string> ValidSigners;
bf6ac7ca
DK
86 size_t buffersize = 0;
87 char *buffer = NULL;
b3d44315
MV
88 while (1)
89 {
bf6ac7ca
DK
90 if (getline(&buffer, &buffersize, pipein) == -1)
91 break;
46e39c8e
MV
92 if (Debug == true)
93 std::clog << "Read: " << buffer << std::endl;
b3d44315
MV
94
95 // Push the data into three separate vectors, which
96 // we later concatenate. They're kept separate so
97 // if we improve the apt method communication stuff later
98 // it will be better.
99 if (strncmp(buffer, GNUPGBADSIG, sizeof(GNUPGBADSIG)-1) == 0)
100 {
46e39c8e
MV
101 if (Debug == true)
102 std::clog << "Got BADSIG! " << std::endl;
b3d44315
MV
103 BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
104 }
76a71a12
JAK
105 else if (strncmp(buffer, GNUPGERRSIG, sizeof(GNUPGERRSIG)-1) == 0)
106 {
107 if (Debug == true)
108 std::clog << "Got ERRSIG! " << std::endl;
109 BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
110 }
4e03c47d 111 else if (strncmp(buffer, GNUPGNOPUBKEY, sizeof(GNUPGNOPUBKEY)-1) == 0)
b3d44315 112 {
46e39c8e
MV
113 if (Debug == true)
114 std::clog << "Got NO_PUBKEY " << std::endl;
b3d44315
MV
115 NoPubKeySigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
116 }
4e03c47d 117 else if (strncmp(buffer, GNUPGNODATA, sizeof(GNUPGBADSIG)-1) == 0)
2abb68b7 118 {
46e39c8e
MV
119 if (Debug == true)
120 std::clog << "Got NODATA! " << std::endl;
2abb68b7
MV
121 BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
122 }
4e03c47d 123 else if (strncmp(buffer, GNUPGKEYEXPIRED, sizeof(GNUPGKEYEXPIRED)-1) == 0)
c5d8878d 124 {
46e39c8e
MV
125 if (Debug == true)
126 std::clog << "Got KEYEXPIRED! " << std::endl;
c5d8878d
MV
127 WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
128 }
4e03c47d 129 else if (strncmp(buffer, GNUPGREVKEYSIG, sizeof(GNUPGREVKEYSIG)-1) == 0)
c5d8878d 130 {
46e39c8e
MV
131 if (Debug == true)
132 std::clog << "Got REVKEYSIG! " << std::endl;
c5d8878d
MV
133 WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
134 }
4e03c47d 135 else if (strncmp(buffer, GNUPGGOODSIG, sizeof(GNUPGGOODSIG)-1) == 0)
b3d44315
MV
136 {
137 char *sig = buffer + sizeof(GNUPGPREFIX);
c5d8878d 138 char *p = sig + sizeof("GOODSIG");
b3d44315
MV
139 while (*p && isxdigit(*p))
140 p++;
141 *p = 0;
46e39c8e
MV
142 if (Debug == true)
143 std::clog << "Got GOODSIG, key ID:" << sig << std::endl;
b3d44315
MV
144 GoodSigners.push_back(string(sig));
145 }
4e03c47d
DK
146 else if (strncmp(buffer, GNUPGVALIDSIG, sizeof(GNUPGVALIDSIG)-1) == 0)
147 {
148 char *sig = buffer + sizeof(GNUPGVALIDSIG);
149 char *p = sig;
150 while (*p && isxdigit(*p))
151 p++;
152 *p = 0;
153 if (Debug == true)
154 std::clog << "Got VALIDSIG, key ID: " << sig << std::endl;
155 ValidSigners.push_back(string(sig));
156 }
b3d44315
MV
157 }
158 fclose(pipein);
1b7bf822 159 free(buffer);
b3d44315 160
4e03c47d
DK
161 // apt-key has a --keyid parameter, but this requires gpg, so we call it without it
162 // and instead check after the fact which keyids where used for verification
163 if (keyIsID == true)
164 {
165 if (Debug == true)
166 std::clog << "GoodSigs needs to be limited to keyid " << key << std::endl;
167 std::vector<std::string>::iterator const foundItr = std::find(ValidSigners.begin(), ValidSigners.end(), key);
168 bool const found = (foundItr != ValidSigners.end());
169 std::copy(GoodSigners.begin(), GoodSigners.end(), std::back_insert_iterator<std::vector<std::string> >(NoPubKeySigners));
170 if (found)
171 {
172 // we look for GOODSIG here as well as an expired sig is a valid sig as well (but not a good one)
173 std::string const goodlongkeyid = "GOODSIG " + key.substr(24, 16);
174 bool const foundGood = std::find(GoodSigners.begin(), GoodSigners.end(), goodlongkeyid) != GoodSigners.end();
175 if (Debug == true)
176 std::clog << "Key " << key << " is valid sig, is " << goodlongkeyid << " also a good one? " << (foundGood ? "yes" : "no") << std::endl;
177 GoodSigners.clear();
178 if (foundGood)
179 {
180 GoodSigners.push_back(goodlongkeyid);
181 NoPubKeySigners.erase(std::remove(NoPubKeySigners.begin(), NoPubKeySigners.end(), goodlongkeyid), NoPubKeySigners.end());
182 }
183 }
184 else
185 GoodSigners.clear();
186 }
187
cf440fac 188 int status;
b3d44315 189 waitpid(pid, &status, 0);
46e39c8e 190 if (Debug == true)
b3d44315 191 {
2737f28a 192 ioprintf(std::clog, "gpgv exited with status %i\n", WEXITSTATUS(status));
b3d44315
MV
193 }
194
195 if (WEXITSTATUS(status) == 0)
196 {
4e03c47d
DK
197 if (keyIsID)
198 {
199 // gpgv will report success, but we want to enforce a certain keyring
200 // so if we haven't found the key the valid we found is in fact invalid
201 if (GoodSigners.empty())
202 return _("At least one invalid signature was encountered.");
203 }
204 else
205 {
206 if (GoodSigners.empty())
207 return _("Internal error: Good signature, but could not determine key fingerprint?!");
208 }
da9ed163 209 return "";
b3d44315
MV
210 }
211 else if (WEXITSTATUS(status) == 1)
339690e4 212 return _("At least one invalid signature was encountered.");
b3d44315 213 else if (WEXITSTATUS(status) == 111)
b39bb552 214 return _("Could not execute 'apt-key' to verify signature (is gnupg installed?)");
ae99ce2e 215 else if (WEXITSTATUS(status) == 112)
b3d44315 216 {
ae99ce2e
DK
217 // acquire system checks for "NODATA" to generate GPG errors (the others are only warnings)
218 std::string errmsg;
219 //TRANSLATORS: %s is a single techy word like 'NODATA'
220 strprintf(errmsg, _("Clearsigned file isn't valid, got '%s' (does the network require authentication?)"), "NODATA");
221 return errmsg;
b3d44315
MV
222 }
223 else
b39bb552 224 return _("Unknown error executing apt-key");
b3d44315
MV
225}
226
b0d40854 227bool GPGVMethod::URIAcquire(std::string const &Message, FetchItem *Itm)
b3d44315 228{
b0d40854
DK
229 URI const Get = Itm->Uri;
230 string const Path = Get.Host + Get.Path; // To account for relative paths
231 std::string const key = LookupTag(Message, "Signed-By");
b3d44315
MV
232 vector<string> GoodSigners;
233 vector<string> BadSigners;
c5d8878d
MV
234 // a worthless signature is a expired or revoked one
235 vector<string> WorthlessSigners;
b3d44315
MV
236 vector<string> NoPubKeySigners;
237
238 FetchResult Res;
239 Res.Filename = Itm->DestFile;
240 URIStart(Res);
241
b39bb552 242 // Run apt-key on file, extract contents and get the key ID of the signer
b0d40854 243 string msg = VerifyGetSigners(Path.c_str(), Itm->DestFile.c_str(), key,
c5d8878d
MV
244 GoodSigners, BadSigners, WorthlessSigners,
245 NoPubKeySigners);
b3d44315
MV
246 if (GoodSigners.empty() || !BadSigners.empty() || !NoPubKeySigners.empty())
247 {
248 string errmsg;
249 // In this case, something bad probably happened, so we just go
250 // with what the other method gave us for an error message.
c5d8878d 251 if (BadSigners.empty() && WorthlessSigners.empty() && NoPubKeySigners.empty())
b3d44315
MV
252 errmsg = msg;
253 else
254 {
255 if (!BadSigners.empty())
256 {
339690e4 257 errmsg += _("The following signatures were invalid:\n");
b3d44315 258 for (vector<string>::iterator I = BadSigners.begin();
f7f0d6c7 259 I != BadSigners.end(); ++I)
b3d44315
MV
260 errmsg += (*I + "\n");
261 }
c5d8878d
MV
262 if (!WorthlessSigners.empty())
263 {
264 errmsg += _("The following signatures were invalid:\n");
265 for (vector<string>::iterator I = WorthlessSigners.begin();
f7f0d6c7 266 I != WorthlessSigners.end(); ++I)
c5d8878d
MV
267 errmsg += (*I + "\n");
268 }
b3d44315
MV
269 if (!NoPubKeySigners.empty())
270 {
339690e4 271 errmsg += _("The following signatures couldn't be verified because the public key is not available:\n");
b3d44315 272 for (vector<string>::iterator I = NoPubKeySigners.begin();
f7f0d6c7 273 I != NoPubKeySigners.end(); ++I)
b3d44315
MV
274 errmsg += (*I + "\n");
275 }
276 }
ce424cd4
MV
277 // this is only fatal if we have no good sigs or if we have at
278 // least one bad signature. good signatures and NoPubKey signatures
279 // happen easily when a file is signed with multiple signatures
280 if(GoodSigners.empty() or !BadSigners.empty())
f23153d0 281 return _error->Error("%s", errmsg.c_str());
b3d44315
MV
282 }
283
b3d44315
MV
284 // Just pass the raw output up, because passing it as a real data
285 // structure is too difficult with the method stuff. We keep it
286 // as three separate vectors for future extensibility.
287 Res.GPGVOutput = GoodSigners;
288 Res.GPGVOutput.insert(Res.GPGVOutput.end(),BadSigners.begin(),BadSigners.end());
289 Res.GPGVOutput.insert(Res.GPGVOutput.end(),NoPubKeySigners.begin(),NoPubKeySigners.end());
290 URIDone(Res);
291
292 if (_config->FindB("Debug::Acquire::gpgv", false))
293 {
b39bb552 294 std::clog << "apt-key succeeded\n";
b3d44315
MV
295 }
296
297 return true;
298}
299
300
301int main()
302{
339690e4 303 setlocale(LC_ALL, "");
3927c6da 304
b3d44315
MV
305 GPGVMethod Mth;
306
307 return Mth.Run();
308}