]> git.saurik.com Git - apt.git/blame - methods/gpgv.cc
do not fail if an unrelated error is pending in DisplayRecord()
[apt.git] / methods / gpgv.cc
CommitLineData
b3d44315
MV
1#include <apt-pkg/error.h>
2#include <apt-pkg/acquire-method.h>
3#include <apt-pkg/strutl.h>
46e39c8e 4#include <apt-pkg/fileutl.h>
339690e4 5#include <apti18n.h>
b3d44315 6
b3d44315
MV
7#include <utime.h>
8#include <stdio.h>
9#include <fcntl.h>
10#include <errno.h>
11#include <sys/wait.h>
12#include <iostream>
da9ed163 13#include <sstream>
b3d44315 14
f5a3d009
DK
15#include <vector>
16
b3d44315
MV
17#define GNUPGPREFIX "[GNUPG:]"
18#define GNUPGBADSIG "[GNUPG:] BADSIG"
19#define GNUPGNOPUBKEY "[GNUPG:] NO_PUBKEY"
20#define GNUPGVALIDSIG "[GNUPG:] VALIDSIG"
c5d8878d
MV
21#define GNUPGGOODSIG "[GNUPG:] GOODSIG"
22#define GNUPGKEYEXPIRED "[GNUPG:] KEYEXPIRED"
23#define GNUPGREVKEYSIG "[GNUPG:] REVKEYSIG"
2abb68b7 24#define GNUPGNODATA "[GNUPG:] NODATA"
b3d44315
MV
25
26class GPGVMethod : public pkgAcqMethod
27{
28 private:
da9ed163 29 string VerifyGetSigners(const char *file, const char *outfile,
c5d8878d
MV
30 vector<string> &GoodSigners,
31 vector<string> &BadSigners,
32 vector<string> &WorthlessSigners,
b3d44315
MV
33 vector<string> &NoPubKeySigners);
34
35 protected:
36 virtual bool Fetch(FetchItem *Itm);
37
38 public:
39
40 GPGVMethod() : pkgAcqMethod("1.0",SingleInstance | SendConfig) {};
41};
42
da9ed163 43string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
b3d44315
MV
44 vector<string> &GoodSigners,
45 vector<string> &BadSigners,
c5d8878d 46 vector<string> &WorthlessSigners,
b3d44315
MV
47 vector<string> &NoPubKeySigners)
48{
46e39c8e 49 bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
da9ed163
MV
50 // setup a (empty) stringstream for formating the return value
51 std::stringstream ret;
b2c22075 52 ret.str("");
da9ed163 53
46e39c8e
MV
54 if (Debug == true)
55 std::clog << "inside VerifyGetSigners" << std::endl;
56
b3d44315
MV
57 pid_t pid;
58 int fd[2];
59 FILE *pipein;
60 int status;
46e39c8e
MV
61 string const gpgvpath = _config->Find("Dir::Bin::gpg", "/usr/bin/gpgv");
62 // FIXME: remove support for deprecated APT::GPGV setting
63 string const trustedFile = _config->FindFile("Dir::Etc::Trusted",
64 _config->Find("APT::GPGV::TrustedKeyring", "/etc/apt/trusted.gpg").c_str());
65 string const trustedPath = _config->FindDir("Dir::Etc::TrustedParts", "/etc/apt/trusted.gpg.d");
66 if (Debug == true)
b3d44315 67 {
46e39c8e
MV
68 std::clog << "gpgv path: " << gpgvpath << std::endl;
69 std::clog << "Keyring file: " << trustedFile << std::endl;
70 std::clog << "Keyring path: " << trustedPath << std::endl;
b3d44315
MV
71 }
72
46e39c8e
MV
73 vector<string> keyrings = GetListOfFilesInDir(trustedPath, "gpg", false);
74 if (FileExists(trustedFile) == true)
75 keyrings.push_back(trustedFile);
76
77 if (keyrings.empty() == true)
da9ed163 78 {
46e39c8e
MV
79 // TRANSLATOR: %s is the trusted keyring parts directory
80 ioprintf(ret, _("No keyring installed in %s."), trustedPath.c_str());
da9ed163
MV
81 return ret.str();
82 }
46e39c8e 83
b3d44315 84 if (pipe(fd) < 0)
b3d44315 85 return "Couldn't create pipe";
b3d44315
MV
86
87 pid = fork();
88 if (pid < 0)
da9ed163 89 return string("Couldn't spawn new process") + strerror(errno);
b3d44315
MV
90 else if (pid == 0)
91 {
f5a3d009
DK
92 std::vector<const char *> Args;
93 Args.reserve(30);
ad6a45d8 94
f5a3d009
DK
95 Args.push_back(gpgvpath.c_str());
96 Args.push_back("--status-fd");
97 Args.push_back("3");
98 Args.push_back("--ignore-time-conflict");
46e39c8e
MV
99 for (vector<string>::const_iterator K = keyrings.begin();
100 K != keyrings.end(); ++K)
101 {
f5a3d009
DK
102 Args.push_back("--keyring");
103 Args.push_back(K->c_str());
46e39c8e 104 }
ad6a45d8
MV
105
106 Configuration::Item const *Opts;
107 Opts = _config->Tree("Acquire::gpgv::Options");
108 if (Opts != 0)
109 {
110 Opts = Opts->Child;
111 for (; Opts != 0; Opts = Opts->Next)
112 {
113 if (Opts->Value.empty() == true)
114 continue;
f5a3d009 115 Args.push_back(Opts->Value.c_str());
ad6a45d8
MV
116 }
117 }
f5a3d009
DK
118 Args.push_back(file);
119 Args.push_back(outfile);
120 Args.push_back(NULL);
ad6a45d8 121
46e39c8e 122 if (Debug == true)
b3d44315 123 {
46e39c8e 124 std::clog << "Preparing to exec: " << gpgvpath;
f5a3d009
DK
125 for(std::vector<const char *>::const_iterator a = Args.begin();*a != NULL; ++a)
126 std::clog << " " << *a;
46e39c8e 127 std::clog << std::endl;
b3d44315 128 }
46e39c8e 129 int const nullfd = open("/dev/null", O_RDONLY);
b3d44315
MV
130 close(fd[0]);
131 // Redirect output to /dev/null; we read from the status fd
132 dup2(nullfd, STDOUT_FILENO);
133 dup2(nullfd, STDERR_FILENO);
134 // Redirect the pipe to the status fd (3)
135 dup2(fd[1], 3);
136
31c64df3
OS
137 putenv((char *)"LANG=");
138 putenv((char *)"LC_ALL=");
139 putenv((char *)"LC_MESSAGES=");
f5a3d009 140 execvp(gpgvpath.c_str(), (char **) &Args[0]);
b3d44315
MV
141
142 exit(111);
143 }
144 close(fd[1]);
145
146 pipein = fdopen(fd[0], "r");
147
148 // Loop over the output of gpgv, and check the signatures.
149 size_t buffersize = 64;
150 char *buffer = (char *) malloc(buffersize);
151 size_t bufferoff = 0;
152 while (1)
153 {
154 int c;
155
156 // Read a line. Sigh.
157 while ((c = getc(pipein)) != EOF && c != '\n')
158 {
159 if (bufferoff == buffersize)
160 buffer = (char *) realloc(buffer, buffersize *= 2);
161 *(buffer+bufferoff) = c;
162 bufferoff++;
163 }
164 if (bufferoff == 0 && c == EOF)
165 break;
166 *(buffer+bufferoff) = '\0';
167 bufferoff = 0;
46e39c8e
MV
168 if (Debug == true)
169 std::clog << "Read: " << buffer << std::endl;
b3d44315
MV
170
171 // Push the data into three separate vectors, which
172 // we later concatenate. They're kept separate so
173 // if we improve the apt method communication stuff later
174 // it will be better.
175 if (strncmp(buffer, GNUPGBADSIG, sizeof(GNUPGBADSIG)-1) == 0)
176 {
46e39c8e
MV
177 if (Debug == true)
178 std::clog << "Got BADSIG! " << std::endl;
b3d44315
MV
179 BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
180 }
181
182 if (strncmp(buffer, GNUPGNOPUBKEY, sizeof(GNUPGNOPUBKEY)-1) == 0)
183 {
46e39c8e
MV
184 if (Debug == true)
185 std::clog << "Got NO_PUBKEY " << std::endl;
b3d44315
MV
186 NoPubKeySigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
187 }
2abb68b7
MV
188 if (strncmp(buffer, GNUPGNODATA, sizeof(GNUPGBADSIG)-1) == 0)
189 {
46e39c8e
MV
190 if (Debug == true)
191 std::clog << "Got NODATA! " << std::endl;
2abb68b7
MV
192 BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
193 }
c5d8878d
MV
194 if (strncmp(buffer, GNUPGKEYEXPIRED, sizeof(GNUPGKEYEXPIRED)-1) == 0)
195 {
46e39c8e
MV
196 if (Debug == true)
197 std::clog << "Got KEYEXPIRED! " << std::endl;
c5d8878d
MV
198 WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
199 }
200 if (strncmp(buffer, GNUPGREVKEYSIG, sizeof(GNUPGREVKEYSIG)-1) == 0)
201 {
46e39c8e
MV
202 if (Debug == true)
203 std::clog << "Got REVKEYSIG! " << std::endl;
c5d8878d
MV
204 WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
205 }
206 if (strncmp(buffer, GNUPGGOODSIG, sizeof(GNUPGGOODSIG)-1) == 0)
b3d44315
MV
207 {
208 char *sig = buffer + sizeof(GNUPGPREFIX);
c5d8878d 209 char *p = sig + sizeof("GOODSIG");
b3d44315
MV
210 while (*p && isxdigit(*p))
211 p++;
212 *p = 0;
46e39c8e
MV
213 if (Debug == true)
214 std::clog << "Got GOODSIG, key ID:" << sig << std::endl;
b3d44315
MV
215 GoodSigners.push_back(string(sig));
216 }
217 }
218 fclose(pipein);
219
220 waitpid(pid, &status, 0);
46e39c8e 221 if (Debug == true)
b3d44315 222 {
46e39c8e 223 std::clog << "gpgv exited\n";
b3d44315
MV
224 }
225
226 if (WEXITSTATUS(status) == 0)
227 {
228 if (GoodSigners.empty())
339690e4 229 return _("Internal error: Good signature, but could not determine key fingerprint?!");
da9ed163 230 return "";
b3d44315
MV
231 }
232 else if (WEXITSTATUS(status) == 1)
233 {
339690e4 234 return _("At least one invalid signature was encountered.");
b3d44315
MV
235 }
236 else if (WEXITSTATUS(status) == 111)
237 {
f83589b5 238 ioprintf(ret, _("Could not execute '%s' to verify signature (is gpgv installed?)"), gpgvpath.c_str());
da9ed163 239 return ret.str();
b3d44315
MV
240 }
241 else
242 {
339690e4 243 return _("Unknown error executing gpgv");
b3d44315
MV
244 }
245}
246
247bool GPGVMethod::Fetch(FetchItem *Itm)
248{
249 URI Get = Itm->Uri;
250 string Path = Get.Host + Get.Path; // To account for relative paths
251 string keyID;
252 vector<string> GoodSigners;
253 vector<string> BadSigners;
c5d8878d
MV
254 // a worthless signature is a expired or revoked one
255 vector<string> WorthlessSigners;
b3d44315
MV
256 vector<string> NoPubKeySigners;
257
258 FetchResult Res;
259 Res.Filename = Itm->DestFile;
260 URIStart(Res);
261
262 // Run gpgv on file, extract contents and get the key ID of the signer
da9ed163 263 string msg = VerifyGetSigners(Path.c_str(), Itm->DestFile.c_str(),
c5d8878d
MV
264 GoodSigners, BadSigners, WorthlessSigners,
265 NoPubKeySigners);
b3d44315
MV
266 if (GoodSigners.empty() || !BadSigners.empty() || !NoPubKeySigners.empty())
267 {
268 string errmsg;
269 // In this case, something bad probably happened, so we just go
270 // with what the other method gave us for an error message.
c5d8878d 271 if (BadSigners.empty() && WorthlessSigners.empty() && NoPubKeySigners.empty())
b3d44315
MV
272 errmsg = msg;
273 else
274 {
275 if (!BadSigners.empty())
276 {
339690e4 277 errmsg += _("The following signatures were invalid:\n");
b3d44315
MV
278 for (vector<string>::iterator I = BadSigners.begin();
279 I != BadSigners.end(); I++)
280 errmsg += (*I + "\n");
281 }
c5d8878d
MV
282 if (!WorthlessSigners.empty())
283 {
284 errmsg += _("The following signatures were invalid:\n");
285 for (vector<string>::iterator I = WorthlessSigners.begin();
286 I != WorthlessSigners.end(); I++)
287 errmsg += (*I + "\n");
288 }
b3d44315
MV
289 if (!NoPubKeySigners.empty())
290 {
339690e4 291 errmsg += _("The following signatures couldn't be verified because the public key is not available:\n");
b3d44315
MV
292 for (vector<string>::iterator I = NoPubKeySigners.begin();
293 I != NoPubKeySigners.end(); I++)
294 errmsg += (*I + "\n");
295 }
296 }
ce424cd4
MV
297 // this is only fatal if we have no good sigs or if we have at
298 // least one bad signature. good signatures and NoPubKey signatures
299 // happen easily when a file is signed with multiple signatures
300 if(GoodSigners.empty() or !BadSigners.empty())
f23153d0 301 return _error->Error("%s", errmsg.c_str());
b3d44315
MV
302 }
303
b3d44315
MV
304 // Just pass the raw output up, because passing it as a real data
305 // structure is too difficult with the method stuff. We keep it
306 // as three separate vectors for future extensibility.
307 Res.GPGVOutput = GoodSigners;
308 Res.GPGVOutput.insert(Res.GPGVOutput.end(),BadSigners.begin(),BadSigners.end());
309 Res.GPGVOutput.insert(Res.GPGVOutput.end(),NoPubKeySigners.begin(),NoPubKeySigners.end());
310 URIDone(Res);
311
312 if (_config->FindB("Debug::Acquire::gpgv", false))
313 {
46e39c8e 314 std::clog << "gpgv succeeded\n";
b3d44315
MV
315 }
316
317 return true;
318}
319
320
321int main()
322{
339690e4
MV
323 setlocale(LC_ALL, "");
324
b3d44315
MV
325 GPGVMethod Mth;
326
327 return Mth.Run();
328}