]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/gpgv.cc
Merge remote-tracking branch 'mvo/debian/sid' into debian/experimental-no-abi-break
[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
2d3fe9cf
DK
106 std::vector<std::string> dataHeader;
107 char * sig = NULL;
108 char * data = NULL;
109
110 // file with detached signature
2f5b6151 111 if (FileGPG != File)
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
2d3fe9cf
DK
184 if (FileGPG != File)
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;
b408e4ad
DK
263 ssize_t line_len = 0;
264 while ((line_len = getline(&buf, &buf_size, in)) != -1)
2d3fe9cf
DK
265 {
266 _strrstrip(buf);
267 if (found_message_start == false)
268 {
269 if (strcmp(buf, "-----BEGIN PGP SIGNED MESSAGE-----") == 0)
270 {
271 found_message_start = true;
272 skip_until_empty_line = true;
273 }
274 }
275 else if (skip_until_empty_line == true)
276 {
277 if (strlen(buf) == 0)
278 skip_until_empty_line = false;
279 // save "Hash" Armor Headers, others aren't allowed
280 else if (ContentHeader != NULL && strncmp(buf, "Hash: ", strlen("Hash: ")) == 0)
281 ContentHeader->push_back(buf);
282 }
283 else if (found_signature == false)
284 {
285 if (strcmp(buf, "-----BEGIN PGP SIGNATURE-----") == 0)
286 {
287 found_signature = true;
288 found_message_end = true;
b408e4ad
DK
289 if (SignatureFile != NULL)
290 {
291 SignatureFile->Write(buf, strlen(buf));
292 SignatureFile->Write("\n", 1);
293 }
2d3fe9cf 294 }
cb323489 295 else if (found_message_end == false) // we are in the message block
2d3fe9cf 296 {
cb323489
DK
297 // we don't have any fields which need dash-escaped,
298 // but implementations are free to encode all lines …
299 char const * dashfree = buf;
300 if (strncmp(dashfree, "- ", 2) == 0)
301 dashfree += 2;
2d3fe9cf 302 if(first_line == true) // first line does not need a newline
2d3fe9cf 303 first_line = false;
b408e4ad 304 else if (ContentFile != NULL)
b408e4ad 305 ContentFile->Write("\n", 1);
cb323489
DK
306 else
307 continue;
308 if (ContentFile != NULL)
309 ContentFile->Write(dashfree, strlen(dashfree));
2d3fe9cf
DK
310 }
311 }
312 else if (found_signature == true)
313 {
b408e4ad
DK
314 if (SignatureFile != NULL)
315 {
316 SignatureFile->Write(buf, strlen(buf));
317 SignatureFile->Write("\n", 1);
318 }
2d3fe9cf
DK
319 if (strcmp(buf, "-----END PGP SIGNATURE-----") == 0)
320 found_signature = false; // look for other signatures
321 }
322 // all the rest is whitespace, unsigned garbage or additional message blocks we ignore
323 }
bea263c2 324 fclose(in);
2d3fe9cf 325
f1828b69
DK
326 if (found_signature == true)
327 return _error->Error("Signature in file %s wasn't closed", InFile.c_str());
328
329 // if we haven't found any of them, this an unsigned file,
330 // so don't generate an error, but splitting was unsuccessful none-the-less
cb323489 331 if (first_line == true && found_message_start == false && found_message_end == false)
f1828b69
DK
332 return false;
333 // otherwise one missing indicates a syntax error
463f24f9
MV
334 else if (first_line == true || found_message_start == false || found_message_end == false)
335 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 336
2d3fe9cf
DK
337 return true;
338}
f1828b69
DK
339 /*}}}*/
340bool OpenMaybeClearSignedFile(std::string const &ClearSignedFileName, FileFd &MessageFile) /*{{{*/
341{
342 char * const message = GenerateTemporaryFileTemplate("fileutl.message");
343 int const messageFd = mkstemp(message);
344 if (messageFd == -1)
345 {
346 free(message);
347 return _error->Errno("mkstemp", "Couldn't create temporary file to work with %s", ClearSignedFileName.c_str());
348 }
349 // we have the fd, thats enough for us
350 unlink(message);
351 free(message);
352
b408e4ad
DK
353 MessageFile.OpenDescriptor(messageFd, FileFd::ReadWrite, true);
354 if (MessageFile.Failed() == true)
355 return _error->Error("Couldn't open temporary file to work with %s", ClearSignedFileName.c_str());
f1828b69
DK
356
357 _error->PushToStack();
b408e4ad 358 bool const splitDone = SplitClearSignedFile(ClearSignedFileName.c_str(), &MessageFile, NULL, NULL);
f1828b69
DK
359 bool const errorDone = _error->PendingError();
360 _error->MergeWithStack();
361 if (splitDone == false)
362 {
b408e4ad 363 MessageFile.Close();
f1828b69
DK
364
365 if (errorDone == true)
366 return false;
367
368 // we deal with an unsigned file
369 MessageFile.Open(ClearSignedFileName, FileFd::ReadOnly);
370 }
371 else // clear-signed
372 {
b408e4ad
DK
373 if (MessageFile.Seek(0) == false)
374 return _error->Errno("lseek", "Unable to seek back in message for file %s", ClearSignedFileName.c_str());
f1828b69
DK
375 }
376
377 return MessageFile.Failed() == false;
378}
379 /*}}}*/