]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/gpgv.cc
miscellaneous small cleanups in apt-key
[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");
12841e83 58 Args.push_back("adv");
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 }
12841e83 80 Args.push_back("--verify");
2f5b6151 81
62d8a765 82 enum { DETACHED, CLEARSIGNED } releaseSignature = (FileGPG != File) ? DETACHED : CLEARSIGNED;
2d3fe9cf
DK
83 std::vector<std::string> dataHeader;
84 char * sig = NULL;
85 char * data = NULL;
86
62d8a765 87 if (releaseSignature == DETACHED)
2d3fe9cf
DK
88 {
89 Args.push_back(FileGPG.c_str());
2f5b6151 90 Args.push_back(File.c_str());
2d3fe9cf
DK
91 }
92 else // clear-signed file
93 {
94 sig = GenerateTemporaryFileTemplate("apt.sig");
95 data = GenerateTemporaryFileTemplate("apt.data");
96 if (sig == NULL || data == NULL)
97 {
b408e4ad
DK
98 ioprintf(std::cerr, "Couldn't create tempfile names for splitting up %s", File.c_str());
99 exit(EINTERNAL);
100 }
101
102 int const sigFd = mkstemp(sig);
103 int const dataFd = mkstemp(data);
104 if (sigFd == -1 || dataFd == -1)
105 {
106 if (dataFd != -1)
107 unlink(sig);
108 if (sigFd != -1)
109 unlink(data);
2d3fe9cf
DK
110 ioprintf(std::cerr, "Couldn't create tempfiles for splitting up %s", File.c_str());
111 exit(EINTERNAL);
112 }
113
b408e4ad
DK
114 FileFd signature;
115 signature.OpenDescriptor(sigFd, FileFd::WriteOnly, true);
116 FileFd message;
117 message.OpenDescriptor(dataFd, FileFd::WriteOnly, true);
2d3fe9cf 118
b408e4ad
DK
119 if (signature.Failed() == true || message.Failed() == true ||
120 SplitClearSignedFile(File, &message, &dataHeader, &signature) == false)
2d3fe9cf
DK
121 {
122 if (dataFd != -1)
123 unlink(sig);
124 if (sigFd != -1)
125 unlink(data);
126 ioprintf(std::cerr, "Splitting up %s into data and signature failed", File.c_str());
ae99ce2e 127 exit(112);
2d3fe9cf 128 }
2d3fe9cf
DK
129 Args.push_back(sig);
130 Args.push_back(data);
131 }
132
2f5b6151
DK
133 Args.push_back(NULL);
134
135 if (Debug == true)
136 {
12841e83 137 std::clog << "Preparing to exec: ";
2f5b6151
DK
138 for (std::vector<const char *>::const_iterator a = Args.begin(); *a != NULL; ++a)
139 std::clog << " " << *a;
140 std::clog << std::endl;
141 }
142
143 if (statusfd != -1)
144 {
12841e83 145 int const nullfd = open("/dev/null", O_WRONLY);
2f5b6151
DK
146 close(fd[0]);
147 // Redirect output to /dev/null; we read from the status fd
b38bb727
DK
148 if (statusfd != STDOUT_FILENO)
149 dup2(nullfd, STDOUT_FILENO);
150 if (statusfd != STDERR_FILENO)
151 dup2(nullfd, STDERR_FILENO);
2f5b6151
DK
152 // Redirect the pipe to the status fd (3)
153 dup2(fd[1], statusfd);
154
155 putenv((char *)"LANG=");
156 putenv((char *)"LC_ALL=");
157 putenv((char *)"LC_MESSAGES=");
158 }
159
62d8a765 160 if (releaseSignature == DETACHED)
2d3fe9cf 161 {
12841e83 162 execvp(Args[0], (char **) &Args[0]);
2d3fe9cf
DK
163 ioprintf(std::cerr, "Couldn't execute %s to check %s", Args[0], File.c_str());
164 exit(EINTERNAL);
165 }
166 else
167 {
168//#define UNLINK_EXIT(X) exit(X)
169#define UNLINK_EXIT(X) unlink(sig);unlink(data);exit(X)
170
171 // for clear-signed files we have created tempfiles we have to clean up
172 // and we do an additional check, so fork yet another time …
173 pid_t pid = ExecFork();
174 if(pid < 0) {
175 ioprintf(std::cerr, "Fork failed for %s to check %s", Args[0], File.c_str());
176 UNLINK_EXIT(EINTERNAL);
177 }
178 if(pid == 0)
179 {
180 if (statusfd != -1)
181 dup2(fd[1], statusfd);
12841e83 182 execvp(Args[0], (char **) &Args[0]);
2d3fe9cf
DK
183 ioprintf(std::cerr, "Couldn't execute %s to check %s", Args[0], File.c_str());
184 UNLINK_EXIT(EINTERNAL);
185 }
186
187 // Wait and collect the error code - taken from WaitPid as we need the exact Status
188 int Status;
189 while (waitpid(pid,&Status,0) != pid)
190 {
191 if (errno == EINTR)
192 continue;
12841e83 193 ioprintf(std::cerr, _("Waited for %s but it wasn't there"), "apt-key");
2d3fe9cf
DK
194 UNLINK_EXIT(EINTERNAL);
195 }
bea263c2 196#undef UNLINK_EXIT
233b7808 197 // we don't need the files any longer
bea263c2
DK
198 unlink(sig);
199 unlink(data);
200 free(sig);
201 free(data);
2d3fe9cf
DK
202
203 // check if it exit'ed normally …
204 if (WIFEXITED(Status) == false)
205 {
12841e83 206 ioprintf(std::cerr, _("Sub-process %s exited unexpectedly"), "apt-key");
bea263c2 207 exit(EINTERNAL);
2d3fe9cf
DK
208 }
209
210 // … and with a good exit code
211 if (WEXITSTATUS(Status) != 0)
212 {
12841e83 213 ioprintf(std::cerr, _("Sub-process %s returned an error code (%u)"), "apt-key", WEXITSTATUS(Status));
bea263c2 214 exit(WEXITSTATUS(Status));
2d3fe9cf
DK
215 }
216
233b7808 217 // everything fine
bea263c2 218 exit(0);
2d3fe9cf
DK
219 }
220 exit(EINTERNAL); // unreachable safe-guard
221}
222 /*}}}*/
2d3fe9cf 223// SplitClearSignedFile - split message into data/signature /*{{{*/
b408e4ad
DK
224bool SplitClearSignedFile(std::string const &InFile, FileFd * const ContentFile,
225 std::vector<std::string> * const ContentHeader, FileFd * const SignatureFile)
2d3fe9cf
DK
226{
227 FILE *in = fopen(InFile.c_str(), "r");
228 if (in == NULL)
229 return _error->Errno("fopen", "can not open %s", InFile.c_str());
230
2d3fe9cf
DK
231 bool found_message_start = false;
232 bool found_message_end = false;
233 bool skip_until_empty_line = false;
234 bool found_signature = false;
235 bool first_line = true;
236
237 char *buf = NULL;
238 size_t buf_size = 0;
9ce3cfc9 239 while (getline(&buf, &buf_size, in) != -1)
2d3fe9cf
DK
240 {
241 _strrstrip(buf);
242 if (found_message_start == false)
243 {
244 if (strcmp(buf, "-----BEGIN PGP SIGNED MESSAGE-----") == 0)
245 {
246 found_message_start = true;
247 skip_until_empty_line = true;
248 }
249 }
250 else if (skip_until_empty_line == true)
251 {
252 if (strlen(buf) == 0)
253 skip_until_empty_line = false;
254 // save "Hash" Armor Headers, others aren't allowed
255 else if (ContentHeader != NULL && strncmp(buf, "Hash: ", strlen("Hash: ")) == 0)
256 ContentHeader->push_back(buf);
257 }
258 else if (found_signature == false)
259 {
260 if (strcmp(buf, "-----BEGIN PGP SIGNATURE-----") == 0)
261 {
262 found_signature = true;
263 found_message_end = true;
b408e4ad
DK
264 if (SignatureFile != NULL)
265 {
266 SignatureFile->Write(buf, strlen(buf));
267 SignatureFile->Write("\n", 1);
268 }
2d3fe9cf 269 }
cb323489 270 else if (found_message_end == false) // we are in the message block
2d3fe9cf 271 {
cb323489
DK
272 // we don't have any fields which need dash-escaped,
273 // but implementations are free to encode all lines …
274 char const * dashfree = buf;
275 if (strncmp(dashfree, "- ", 2) == 0)
276 dashfree += 2;
2d3fe9cf 277 if(first_line == true) // first line does not need a newline
2d3fe9cf 278 first_line = false;
b408e4ad 279 else if (ContentFile != NULL)
b408e4ad 280 ContentFile->Write("\n", 1);
cb323489
DK
281 else
282 continue;
283 if (ContentFile != NULL)
284 ContentFile->Write(dashfree, strlen(dashfree));
2d3fe9cf
DK
285 }
286 }
287 else if (found_signature == true)
288 {
b408e4ad
DK
289 if (SignatureFile != NULL)
290 {
291 SignatureFile->Write(buf, strlen(buf));
292 SignatureFile->Write("\n", 1);
293 }
2d3fe9cf
DK
294 if (strcmp(buf, "-----END PGP SIGNATURE-----") == 0)
295 found_signature = false; // look for other signatures
296 }
297 // all the rest is whitespace, unsigned garbage or additional message blocks we ignore
298 }
bea263c2 299 fclose(in);
2d3fe9cf 300
f1828b69
DK
301 if (found_signature == true)
302 return _error->Error("Signature in file %s wasn't closed", InFile.c_str());
303
304 // if we haven't found any of them, this an unsigned file,
305 // so don't generate an error, but splitting was unsuccessful none-the-less
cb323489 306 if (first_line == true && found_message_start == false && found_message_end == false)
f1828b69
DK
307 return false;
308 // otherwise one missing indicates a syntax error
463f24f9
MV
309 else if (first_line == true || found_message_start == false || found_message_end == false)
310 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 311
2d3fe9cf
DK
312 return true;
313}
f1828b69
DK
314 /*}}}*/
315bool OpenMaybeClearSignedFile(std::string const &ClearSignedFileName, FileFd &MessageFile) /*{{{*/
316{
317 char * const message = GenerateTemporaryFileTemplate("fileutl.message");
318 int const messageFd = mkstemp(message);
319 if (messageFd == -1)
320 {
321 free(message);
322 return _error->Errno("mkstemp", "Couldn't create temporary file to work with %s", ClearSignedFileName.c_str());
323 }
324 // we have the fd, thats enough for us
325 unlink(message);
326 free(message);
327
b408e4ad
DK
328 MessageFile.OpenDescriptor(messageFd, FileFd::ReadWrite, true);
329 if (MessageFile.Failed() == true)
330 return _error->Error("Couldn't open temporary file to work with %s", ClearSignedFileName.c_str());
f1828b69
DK
331
332 _error->PushToStack();
9ce3cfc9 333 bool const splitDone = SplitClearSignedFile(ClearSignedFileName, &MessageFile, NULL, NULL);
f1828b69
DK
334 bool const errorDone = _error->PendingError();
335 _error->MergeWithStack();
336 if (splitDone == false)
337 {
b408e4ad 338 MessageFile.Close();
f1828b69
DK
339
340 if (errorDone == true)
341 return false;
342
343 // we deal with an unsigned file
344 MessageFile.Open(ClearSignedFileName, FileFd::ReadOnly);
345 }
346 else // clear-signed
347 {
b408e4ad
DK
348 if (MessageFile.Seek(0) == false)
349 return _error->Errno("lseek", "Unable to seek back in message for file %s", ClearSignedFileName.c_str());
f1828b69
DK
350 }
351
352 return MessageFile.Failed() == false;
353}
354 /*}}}*/