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