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