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