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