]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/gpgv.cc
Fix handling of autoclosing for compressed files (Closes: #741685)
[apt.git] / apt-pkg / contrib / gpgv.cc
CommitLineData
2f5b6151
DK
1// -*- mode: cpp; mode: fold -*-
2// Include Files /*{{{*/
3#include<config.h>
4
453b82a3
DK
5#include<apt-pkg/configuration.h>
6#include<apt-pkg/error.h>
7#include<apt-pkg/strutl.h>
8#include<apt-pkg/fileutl.h>
9#include<apt-pkg/gpgv.h>
10
2d3fe9cf 11#include <errno.h>
b38bb727 12#include <stdio.h>
2d3fe9cf 13#include <string.h>
b38bb727 14#include <stdlib.h>
2d3fe9cf 15#include <fcntl.h>
2d3fe9cf 16#include <sys/wait.h>
38beb8b5 17#include <unistd.h>
453b82a3
DK
18#include <stddef.h>
19#include <iostream>
20#include <string>
21#include <vector>
2f5b6151
DK
22
23#include <apti18n.h>
24 /*}}}*/
f1828b69 25static char * GenerateTemporaryFileTemplate(const char *basename) /*{{{*/
2d3fe9cf 26{
2d3fe9cf 27 std::string out;
68e01721
MV
28 std::string tmpdir = GetTempDir();
29 strprintf(out, "%s/%s.XXXXXX", tmpdir.c_str(), basename);
2d3fe9cf
DK
30 return strdup(out.c_str());
31}
32 /*}}}*/
33// ExecGPGV - returns the command needed for verify /*{{{*/
2f5b6151
DK
34// ---------------------------------------------------------------------
35/* Generating the commandline for calling gpgv is somehow complicated as
2d3fe9cf
DK
36 we need to add multiple keyrings and user supplied options.
37 Also, as gpgv has no options to enforce a certain reduced style of
38 clear-signed files (=the complete content of the file is signed and
39 the content isn't encoded) we do a divide and conquer approach here
b408e4ad 40 and split up the clear-signed file in message and signature for gpgv
2d3fe9cf 41*/
99ed26d3 42void ExecGPGV(std::string const &File, std::string const &FileGPG,
2d3fe9cf 43 int const &statusfd, int fd[2])
2f5b6151 44{
99ed26d3 45 #define EINTERNAL 111
2d3fe9cf 46 std::string const gpgvpath = _config->Find("Dir::Bin::gpg", "/usr/bin/gpgv");
2f5b6151 47 // FIXME: remove support for deprecated APT::GPGV setting
2d3fe9cf
DK
48 std::string const trustedFile = _config->Find("APT::GPGV::TrustedKeyring", _config->FindFile("Dir::Etc::Trusted"));
49 std::string const trustedPath = _config->FindDir("Dir::Etc::TrustedParts");
2f5b6151
DK
50
51 bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
52
53 if (Debug == true)
54 {
55 std::clog << "gpgv path: " << gpgvpath << std::endl;
56 std::clog << "Keyring file: " << trustedFile << std::endl;
57 std::clog << "Keyring path: " << trustedPath << std::endl;
58 }
59
2d3fe9cf 60 std::vector<std::string> keyrings;
2f5b6151
DK
61 if (DirectoryExists(trustedPath))
62 keyrings = GetListOfFilesInDir(trustedPath, "gpg", false, true);
63 if (RealFileExists(trustedFile) == true)
64 keyrings.push_back(trustedFile);
65
66 std::vector<const char *> Args;
67 Args.reserve(30);
68
69 if (keyrings.empty() == true)
70 {
71 // TRANSLATOR: %s is the trusted keyring parts directory
99ed26d3
DK
72 ioprintf(std::cerr, _("No keyring installed in %s."),
73 _config->FindDir("Dir::Etc::TrustedParts").c_str());
74 exit(EINTERNAL);
2f5b6151
DK
75 }
76
77 Args.push_back(gpgvpath.c_str());
78 Args.push_back("--ignore-time-conflict");
79
b38bb727 80 char statusfdstr[10];
2f5b6151
DK
81 if (statusfd != -1)
82 {
83 Args.push_back("--status-fd");
2d3fe9cf 84 snprintf(statusfdstr, sizeof(statusfdstr), "%i", statusfd);
b38bb727 85 Args.push_back(statusfdstr);
2f5b6151
DK
86 }
87
2d3fe9cf 88 for (std::vector<std::string>::const_iterator K = keyrings.begin();
2f5b6151
DK
89 K != keyrings.end(); ++K)
90 {
91 Args.push_back("--keyring");
92 Args.push_back(K->c_str());
93 }
94
95 Configuration::Item const *Opts;
96 Opts = _config->Tree("Acquire::gpgv::Options");
97 if (Opts != 0)
98 {
99 Opts = Opts->Child;
100 for (; Opts != 0; Opts = Opts->Next)
101 {
102 if (Opts->Value.empty() == true)
103 continue;
104 Args.push_back(Opts->Value.c_str());
105 }
106 }
107
62d8a765 108 enum { DETACHED, CLEARSIGNED } releaseSignature = (FileGPG != File) ? DETACHED : CLEARSIGNED;
2d3fe9cf
DK
109 std::vector<std::string> dataHeader;
110 char * sig = NULL;
111 char * data = NULL;
112
62d8a765 113 if (releaseSignature == DETACHED)
2d3fe9cf
DK
114 {
115 Args.push_back(FileGPG.c_str());
2f5b6151 116 Args.push_back(File.c_str());
2d3fe9cf
DK
117 }
118 else // clear-signed file
119 {
120 sig = GenerateTemporaryFileTemplate("apt.sig");
121 data = GenerateTemporaryFileTemplate("apt.data");
122 if (sig == NULL || data == NULL)
123 {
b408e4ad
DK
124 ioprintf(std::cerr, "Couldn't create tempfile names for splitting up %s", File.c_str());
125 exit(EINTERNAL);
126 }
127
128 int const sigFd = mkstemp(sig);
129 int const dataFd = mkstemp(data);
130 if (sigFd == -1 || dataFd == -1)
131 {
132 if (dataFd != -1)
133 unlink(sig);
134 if (sigFd != -1)
135 unlink(data);
2d3fe9cf
DK
136 ioprintf(std::cerr, "Couldn't create tempfiles for splitting up %s", File.c_str());
137 exit(EINTERNAL);
138 }
139
b408e4ad
DK
140 FileFd signature;
141 signature.OpenDescriptor(sigFd, FileFd::WriteOnly, true);
142 FileFd message;
143 message.OpenDescriptor(dataFd, FileFd::WriteOnly, true);
2d3fe9cf 144
b408e4ad
DK
145 if (signature.Failed() == true || message.Failed() == true ||
146 SplitClearSignedFile(File, &message, &dataHeader, &signature) == false)
2d3fe9cf
DK
147 {
148 if (dataFd != -1)
149 unlink(sig);
150 if (sigFd != -1)
151 unlink(data);
152 ioprintf(std::cerr, "Splitting up %s into data and signature failed", File.c_str());
ae99ce2e 153 exit(112);
2d3fe9cf 154 }
2d3fe9cf
DK
155 Args.push_back(sig);
156 Args.push_back(data);
157 }
158
2f5b6151
DK
159 Args.push_back(NULL);
160
161 if (Debug == true)
162 {
163 std::clog << "Preparing to exec: " << gpgvpath;
164 for (std::vector<const char *>::const_iterator a = Args.begin(); *a != NULL; ++a)
165 std::clog << " " << *a;
166 std::clog << std::endl;
167 }
168
169 if (statusfd != -1)
170 {
171 int const nullfd = open("/dev/null", O_RDONLY);
172 close(fd[0]);
173 // Redirect output to /dev/null; we read from the status fd
b38bb727
DK
174 if (statusfd != STDOUT_FILENO)
175 dup2(nullfd, STDOUT_FILENO);
176 if (statusfd != STDERR_FILENO)
177 dup2(nullfd, STDERR_FILENO);
2f5b6151
DK
178 // Redirect the pipe to the status fd (3)
179 dup2(fd[1], statusfd);
180
181 putenv((char *)"LANG=");
182 putenv((char *)"LC_ALL=");
183 putenv((char *)"LC_MESSAGES=");
184 }
185
62d8a765 186 if (releaseSignature == DETACHED)
2d3fe9cf
DK
187 {
188 execvp(gpgvpath.c_str(), (char **) &Args[0]);
189 ioprintf(std::cerr, "Couldn't execute %s to check %s", Args[0], File.c_str());
190 exit(EINTERNAL);
191 }
192 else
193 {
194//#define UNLINK_EXIT(X) exit(X)
195#define UNLINK_EXIT(X) unlink(sig);unlink(data);exit(X)
196
197 // for clear-signed files we have created tempfiles we have to clean up
198 // and we do an additional check, so fork yet another time …
199 pid_t pid = ExecFork();
200 if(pid < 0) {
201 ioprintf(std::cerr, "Fork failed for %s to check %s", Args[0], File.c_str());
202 UNLINK_EXIT(EINTERNAL);
203 }
204 if(pid == 0)
205 {
206 if (statusfd != -1)
207 dup2(fd[1], statusfd);
208 execvp(gpgvpath.c_str(), (char **) &Args[0]);
209 ioprintf(std::cerr, "Couldn't execute %s to check %s", Args[0], File.c_str());
210 UNLINK_EXIT(EINTERNAL);
211 }
212
213 // Wait and collect the error code - taken from WaitPid as we need the exact Status
214 int Status;
215 while (waitpid(pid,&Status,0) != pid)
216 {
217 if (errno == EINTR)
218 continue;
219 ioprintf(std::cerr, _("Waited for %s but it wasn't there"), "gpgv");
220 UNLINK_EXIT(EINTERNAL);
221 }
bea263c2 222#undef UNLINK_EXIT
233b7808 223 // we don't need the files any longer
bea263c2
DK
224 unlink(sig);
225 unlink(data);
226 free(sig);
227 free(data);
2d3fe9cf
DK
228
229 // check if it exit'ed normally …
230 if (WIFEXITED(Status) == false)
231 {
232 ioprintf(std::cerr, _("Sub-process %s exited unexpectedly"), "gpgv");
bea263c2 233 exit(EINTERNAL);
2d3fe9cf
DK
234 }
235
236 // … and with a good exit code
237 if (WEXITSTATUS(Status) != 0)
238 {
239 ioprintf(std::cerr, _("Sub-process %s returned an error code (%u)"), "gpgv", WEXITSTATUS(Status));
bea263c2 240 exit(WEXITSTATUS(Status));
2d3fe9cf
DK
241 }
242
233b7808 243 // everything fine
bea263c2 244 exit(0);
2d3fe9cf
DK
245 }
246 exit(EINTERNAL); // unreachable safe-guard
247}
248 /*}}}*/
2d3fe9cf 249// SplitClearSignedFile - split message into data/signature /*{{{*/
b408e4ad
DK
250bool SplitClearSignedFile(std::string const &InFile, FileFd * const ContentFile,
251 std::vector<std::string> * const ContentHeader, FileFd * const SignatureFile)
2d3fe9cf
DK
252{
253 FILE *in = fopen(InFile.c_str(), "r");
254 if (in == NULL)
255 return _error->Errno("fopen", "can not open %s", InFile.c_str());
256
2d3fe9cf
DK
257 bool found_message_start = false;
258 bool found_message_end = false;
259 bool skip_until_empty_line = false;
260 bool found_signature = false;
261 bool first_line = true;
262
263 char *buf = NULL;
264 size_t buf_size = 0;
9ce3cfc9 265 while (getline(&buf, &buf_size, in) != -1)
2d3fe9cf
DK
266 {
267 _strrstrip(buf);
268 if (found_message_start == false)
269 {
270 if (strcmp(buf, "-----BEGIN PGP SIGNED MESSAGE-----") == 0)
271 {
272 found_message_start = true;
273 skip_until_empty_line = true;
274 }
275 }
276 else if (skip_until_empty_line == true)
277 {
278 if (strlen(buf) == 0)
279 skip_until_empty_line = false;
280 // save "Hash" Armor Headers, others aren't allowed
281 else if (ContentHeader != NULL && strncmp(buf, "Hash: ", strlen("Hash: ")) == 0)
282 ContentHeader->push_back(buf);
283 }
284 else if (found_signature == false)
285 {
286 if (strcmp(buf, "-----BEGIN PGP SIGNATURE-----") == 0)
287 {
288 found_signature = true;
289 found_message_end = true;
b408e4ad
DK
290 if (SignatureFile != NULL)
291 {
292 SignatureFile->Write(buf, strlen(buf));
293 SignatureFile->Write("\n", 1);
294 }
2d3fe9cf 295 }
cb323489 296 else if (found_message_end == false) // we are in the message block
2d3fe9cf 297 {
cb323489
DK
298 // we don't have any fields which need dash-escaped,
299 // but implementations are free to encode all lines …
300 char const * dashfree = buf;
301 if (strncmp(dashfree, "- ", 2) == 0)
302 dashfree += 2;
2d3fe9cf 303 if(first_line == true) // first line does not need a newline
2d3fe9cf 304 first_line = false;
b408e4ad 305 else if (ContentFile != NULL)
b408e4ad 306 ContentFile->Write("\n", 1);
cb323489
DK
307 else
308 continue;
309 if (ContentFile != NULL)
310 ContentFile->Write(dashfree, strlen(dashfree));
2d3fe9cf
DK
311 }
312 }
313 else if (found_signature == true)
314 {
b408e4ad
DK
315 if (SignatureFile != NULL)
316 {
317 SignatureFile->Write(buf, strlen(buf));
318 SignatureFile->Write("\n", 1);
319 }
2d3fe9cf
DK
320 if (strcmp(buf, "-----END PGP SIGNATURE-----") == 0)
321 found_signature = false; // look for other signatures
322 }
323 // all the rest is whitespace, unsigned garbage or additional message blocks we ignore
324 }
bea263c2 325 fclose(in);
2d3fe9cf 326
f1828b69
DK
327 if (found_signature == true)
328 return _error->Error("Signature in file %s wasn't closed", InFile.c_str());
329
330 // if we haven't found any of them, this an unsigned file,
331 // so don't generate an error, but splitting was unsuccessful none-the-less
cb323489 332 if (first_line == true && found_message_start == false && found_message_end == false)
f1828b69
DK
333 return false;
334 // otherwise one missing indicates a syntax error
463f24f9
MV
335 else if (first_line == true || found_message_start == false || found_message_end == false)
336 return _error->Error("Splitting of file %s failed as it doesn't contain all expected parts %i %i %i", InFile.c_str(), first_line, found_message_start, found_message_end);
f1828b69 337
2d3fe9cf
DK
338 return true;
339}
f1828b69
DK
340 /*}}}*/
341bool OpenMaybeClearSignedFile(std::string const &ClearSignedFileName, FileFd &MessageFile) /*{{{*/
342{
343 char * const message = GenerateTemporaryFileTemplate("fileutl.message");
344 int const messageFd = mkstemp(message);
345 if (messageFd == -1)
346 {
347 free(message);
348 return _error->Errno("mkstemp", "Couldn't create temporary file to work with %s", ClearSignedFileName.c_str());
349 }
350 // we have the fd, thats enough for us
351 unlink(message);
352 free(message);
353
b408e4ad
DK
354 MessageFile.OpenDescriptor(messageFd, FileFd::ReadWrite, true);
355 if (MessageFile.Failed() == true)
356 return _error->Error("Couldn't open temporary file to work with %s", ClearSignedFileName.c_str());
f1828b69
DK
357
358 _error->PushToStack();
9ce3cfc9 359 bool const splitDone = SplitClearSignedFile(ClearSignedFileName, &MessageFile, NULL, NULL);
f1828b69
DK
360 bool const errorDone = _error->PendingError();
361 _error->MergeWithStack();
362 if (splitDone == false)
363 {
b408e4ad 364 MessageFile.Close();
f1828b69
DK
365
366 if (errorDone == true)
367 return false;
368
369 // we deal with an unsigned file
370 MessageFile.Open(ClearSignedFileName, FileFd::ReadOnly);
371 }
372 else // clear-signed
373 {
b408e4ad
DK
374 if (MessageFile.Seek(0) == false)
375 return _error->Errno("lseek", "Unable to seek back in message for file %s", ClearSignedFileName.c_str());
f1828b69
DK
376 }
377
378 return MessageFile.Failed() == false;
379}
380 /*}}}*/