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