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