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