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