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