]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/contrib/gpgv.cc
use buffered writing for InRelease splitting
[apt.git] / apt-pkg / contrib / gpgv.cc
... / ...
CommitLineData
1// -*- mode: cpp; mode: fold -*-
2// Include Files /*{{{*/
3#include<config.h>
4
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
11#include <errno.h>
12#include <stdio.h>
13#include <string.h>
14#include <stdlib.h>
15#include <fcntl.h>
16#include <sys/wait.h>
17#include <unistd.h>
18#include <stddef.h>
19
20#include <algorithm>
21#include <iostream>
22#include <string>
23#include <vector>
24
25#include <apti18n.h>
26 /*}}}*/
27static char * GenerateTemporaryFileTemplate(const char *basename) /*{{{*/
28{
29 std::string out;
30 std::string tmpdir = GetTempDir();
31 strprintf(out, "%s/%s.XXXXXX", tmpdir.c_str(), basename);
32 return strdup(out.c_str());
33}
34 /*}}}*/
35// ExecGPGV - returns the command needed for verify /*{{{*/
36// ---------------------------------------------------------------------
37/* Generating the commandline for calling gpg is somehow complicated as
38 we need to add multiple keyrings and user supplied options.
39 Also, as gpg has no options to enforce a certain reduced style of
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
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.
45*/
46void ExecGPGV(std::string const &File, std::string const &FileGPG,
47 int const &statusfd, int fd[2], std::string const &key)
48{
49 #define EINTERNAL 111
50 std::string const aptkey = _config->FindFile("Dir::Bin::apt-key", "/usr/bin/apt-key");
51
52 bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
53
54 std::vector<const char *> Args;
55 Args.reserve(10);
56
57 Args.push_back(aptkey.c_str());
58 Args.push_back("--quiet");
59 Args.push_back("--readonly");
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 }
73 Args.push_back("verify");
74
75 char statusfdstr[10];
76 if (statusfd != -1)
77 {
78 Args.push_back("--status-fd");
79 snprintf(statusfdstr, sizeof(statusfdstr), "%i", statusfd);
80 Args.push_back(statusfdstr);
81 }
82
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
96 enum { DETACHED, CLEARSIGNED } releaseSignature = (FileGPG != File) ? DETACHED : CLEARSIGNED;
97 std::vector<std::string> dataHeader;
98 char * sig = NULL;
99 char * data = NULL;
100
101 if (releaseSignature == DETACHED)
102 {
103 Args.push_back(FileGPG.c_str());
104 Args.push_back(File.c_str());
105 }
106 else // clear-signed file
107 {
108 sig = GenerateTemporaryFileTemplate("apt.sig");
109 data = GenerateTemporaryFileTemplate("apt.data");
110 if (sig == NULL || data == NULL)
111 {
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);
124 ioprintf(std::cerr, "Couldn't create tempfiles for splitting up %s", File.c_str());
125 exit(EINTERNAL);
126 }
127
128 FileFd signature;
129 signature.OpenDescriptor(sigFd, FileFd::WriteOnly, true);
130 FileFd message;
131 message.OpenDescriptor(dataFd, FileFd::WriteOnly, true);
132
133 if (signature.Failed() == true || message.Failed() == true ||
134 SplitClearSignedFile(File, &message, &dataHeader, &signature) == false)
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());
141 exit(112);
142 }
143 Args.push_back(sig);
144 Args.push_back(data);
145 }
146
147 Args.push_back(NULL);
148
149 if (Debug == true)
150 {
151 std::clog << "Preparing to exec: ";
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 {
159 int const nullfd = open("/dev/null", O_WRONLY);
160 close(fd[0]);
161 // Redirect output to /dev/null; we read from the status fd
162 if (statusfd != STDOUT_FILENO)
163 dup2(nullfd, STDOUT_FILENO);
164 if (statusfd != STDERR_FILENO)
165 dup2(nullfd, STDERR_FILENO);
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
174 if (releaseSignature == DETACHED)
175 {
176 execvp(Args[0], (char **) &Args[0]);
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);
196 execvp(Args[0], (char **) &Args[0]);
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;
207 ioprintf(std::cerr, _("Waited for %s but it wasn't there"), "apt-key");
208 UNLINK_EXIT(EINTERNAL);
209 }
210#undef UNLINK_EXIT
211 // we don't need the files any longer
212 unlink(sig);
213 unlink(data);
214 free(sig);
215 free(data);
216
217 // check if it exit'ed normally …
218 if (WIFEXITED(Status) == false)
219 {
220 ioprintf(std::cerr, _("Sub-process %s exited unexpectedly"), "apt-key");
221 exit(EINTERNAL);
222 }
223
224 // … and with a good exit code
225 if (WEXITSTATUS(Status) != 0)
226 {
227 ioprintf(std::cerr, _("Sub-process %s returned an error code (%u)"), "apt-key", WEXITSTATUS(Status));
228 exit(WEXITSTATUS(Status));
229 }
230
231 // everything fine
232 exit(0);
233 }
234 exit(EINTERNAL); // unreachable safe-guard
235}
236 /*}}}*/
237// SplitClearSignedFile - split message into data/signature /*{{{*/
238bool SplitClearSignedFile(std::string const &InFile, FileFd * const ContentFile,
239 std::vector<std::string> * const ContentHeader, FileFd * const SignatureFile)
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
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;
253 while (getline(&buf, &buf_size, in) != -1)
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;
278 if (SignatureFile != NULL)
279 {
280 SignatureFile->Write(buf, strlen(buf));
281 SignatureFile->Write("\n", 1);
282 }
283 }
284 else if (found_message_end == false) // we are in the message block
285 {
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;
291 if(first_line == true) // first line does not need a newline
292 first_line = false;
293 else if (ContentFile != NULL)
294 ContentFile->Write("\n", 1);
295 else
296 continue;
297 if (ContentFile != NULL)
298 ContentFile->Write(dashfree, strlen(dashfree));
299 }
300 }
301 else if (found_signature == true)
302 {
303 if (SignatureFile != NULL)
304 {
305 SignatureFile->Write(buf, strlen(buf));
306 SignatureFile->Write("\n", 1);
307 }
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 }
313 fclose(in);
314 if (buf != NULL)
315 free(buf);
316
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
322 if (first_line == true && found_message_start == false && found_message_end == false)
323 return false;
324 // otherwise one missing indicates a syntax error
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);
327
328 return true;
329}
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
344 MessageFile.OpenDescriptor(messageFd, FileFd::ReadWrite | FileFd::BufferedWrite, true);
345 if (MessageFile.Failed() == true)
346 return _error->Error("Couldn't open temporary file to work with %s", ClearSignedFileName.c_str());
347
348 _error->PushToStack();
349 bool const splitDone = SplitClearSignedFile(ClearSignedFileName, &MessageFile, NULL, NULL);
350 bool const errorDone = _error->PendingError();
351 _error->MergeWithStack();
352 if (splitDone == false)
353 {
354 MessageFile.Close();
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 {
364 if (MessageFile.Seek(0) == false)
365 return _error->Errno("lseek", "Unable to seek back in message for file %s", ClearSignedFileName.c_str());
366 }
367
368 return MessageFile.Failed() == false;
369}
370 /*}}}*/