merge with lp:~mvo/apt/debian-sid to get 0.7.25.1 and my changes back
[ntk/apt.git] / methods / gpgv.cc
1 #include <apt-pkg/error.h>
2 #include <apt-pkg/acquire-method.h>
3 #include <apt-pkg/strutl.h>
4 #include <apt-pkg/fileutl.h>
5 #include <apti18n.h>
6
7 #include <utime.h>
8 #include <stdio.h>
9 #include <fcntl.h>
10 #include <errno.h>
11 #include <sys/wait.h>
12 #include <iostream>
13 #include <sstream>
14
15 #define GNUPGPREFIX "[GNUPG:]"
16 #define GNUPGBADSIG "[GNUPG:] BADSIG"
17 #define GNUPGNOPUBKEY "[GNUPG:] NO_PUBKEY"
18 #define GNUPGVALIDSIG "[GNUPG:] VALIDSIG"
19 #define GNUPGGOODSIG "[GNUPG:] GOODSIG"
20 #define GNUPGKEYEXPIRED "[GNUPG:] KEYEXPIRED"
21 #define GNUPGREVKEYSIG "[GNUPG:] REVKEYSIG"
22 #define GNUPGNODATA "[GNUPG:] NODATA"
23
24 class GPGVMethod : public pkgAcqMethod
25 {
26 private:
27 string VerifyGetSigners(const char *file, const char *outfile,
28 vector<string> &GoodSigners,
29 vector<string> &BadSigners,
30 vector<string> &WorthlessSigners,
31 vector<string> &NoPubKeySigners);
32
33 protected:
34 virtual bool Fetch(FetchItem *Itm);
35
36 public:
37
38 GPGVMethod() : pkgAcqMethod("1.0",SingleInstance | SendConfig) {};
39 };
40
41 string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
42 vector<string> &GoodSigners,
43 vector<string> &BadSigners,
44 vector<string> &WorthlessSigners,
45 vector<string> &NoPubKeySigners)
46 {
47 bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
48 // setup a (empty) stringstream for formating the return value
49 std::stringstream ret;
50 ret.str("");
51
52 if (Debug == true)
53 std::clog << "inside VerifyGetSigners" << std::endl;
54
55 pid_t pid;
56 int fd[2];
57 FILE *pipein;
58 int status;
59 string const gpgvpath = _config->Find("Dir::Bin::gpg", "/usr/bin/gpgv");
60 // FIXME: remove support for deprecated APT::GPGV setting
61 string const trustedFile = _config->FindFile("Dir::Etc::Trusted",
62 _config->Find("APT::GPGV::TrustedKeyring", "/etc/apt/trusted.gpg").c_str());
63 string const trustedPath = _config->FindDir("Dir::Etc::TrustedParts", "/etc/apt/trusted.gpg.d");
64 if (Debug == true)
65 {
66 std::clog << "gpgv path: " << gpgvpath << std::endl;
67 std::clog << "Keyring file: " << trustedFile << std::endl;
68 std::clog << "Keyring path: " << trustedPath << std::endl;
69 }
70
71 vector<string> keyrings = GetListOfFilesInDir(trustedPath, "gpg", false);
72 if (FileExists(trustedFile) == true)
73 keyrings.push_back(trustedFile);
74
75 if (keyrings.empty() == true)
76 {
77 // TRANSLATOR: %s is the trusted keyring parts directory
78 ioprintf(ret, _("No keyring installed in %s."), trustedPath.c_str());
79 return ret.str();
80 }
81
82 if (pipe(fd) < 0)
83 return "Couldn't create pipe";
84
85 pid = fork();
86 if (pid < 0)
87 return string("Couldn't spawn new process") + strerror(errno);
88 else if (pid == 0)
89 {
90 const char *Args[400];
91 unsigned int i = 0;
92
93 Args[i++] = gpgvpath.c_str();
94 Args[i++] = "--status-fd";
95 Args[i++] = "3";
96 Args[i++] = "--ignore-time-conflict";
97 for (vector<string>::const_iterator K = keyrings.begin();
98 K != keyrings.end(); ++K)
99 {
100 Args[i++] = "--keyring";
101 Args[i++] = K->c_str();
102 // check overflow (minus a bit of extra space at the end)
103 if(i >= sizeof(Args)/sizeof(char*)-5) {
104 std::clog << _("E: Too many keyrings should be passed to gpgv. Exiting.") << std::endl;
105 exit(111);
106 }
107 }
108
109 Configuration::Item const *Opts;
110 Opts = _config->Tree("Acquire::gpgv::Options");
111 if (Opts != 0)
112 {
113 Opts = Opts->Child;
114 for (; Opts != 0; Opts = Opts->Next)
115 {
116 if (Opts->Value.empty() == true)
117 continue;
118 Args[i++] = Opts->Value.c_str();
119 // check overflow (minus a bit of extra space at the end)
120 if(i >= sizeof(Args)/sizeof(char*)-5) {
121 std::clog << _("E: Argument list from Acquire::gpgv::Options too long. Exiting.") << std::endl;
122 exit(111);
123 }
124 }
125 }
126 Args[i++] = file;
127 Args[i++] = outfile;
128 Args[i++] = NULL;
129
130 if (Debug == true)
131 {
132 std::clog << "Preparing to exec: " << gpgvpath;
133 for(unsigned int j=0;Args[j] != NULL; j++)
134 std::clog << " " << Args[j];
135 std::clog << std::endl;
136 }
137 int const nullfd = open("/dev/null", O_RDONLY);
138 close(fd[0]);
139 // Redirect output to /dev/null; we read from the status fd
140 dup2(nullfd, STDOUT_FILENO);
141 dup2(nullfd, STDERR_FILENO);
142 // Redirect the pipe to the status fd (3)
143 dup2(fd[1], 3);
144
145 putenv((char *)"LANG=");
146 putenv((char *)"LC_ALL=");
147 putenv((char *)"LC_MESSAGES=");
148 execvp(gpgvpath.c_str(), (char **)Args);
149
150 exit(111);
151 }
152 close(fd[1]);
153
154 pipein = fdopen(fd[0], "r");
155
156 // Loop over the output of gpgv, and check the signatures.
157 size_t buffersize = 64;
158 char *buffer = (char *) malloc(buffersize);
159 size_t bufferoff = 0;
160 while (1)
161 {
162 int c;
163
164 // Read a line. Sigh.
165 while ((c = getc(pipein)) != EOF && c != '\n')
166 {
167 if (bufferoff == buffersize)
168 buffer = (char *) realloc(buffer, buffersize *= 2);
169 *(buffer+bufferoff) = c;
170 bufferoff++;
171 }
172 if (bufferoff == 0 && c == EOF)
173 break;
174 *(buffer+bufferoff) = '\0';
175 bufferoff = 0;
176 if (Debug == true)
177 std::clog << "Read: " << buffer << std::endl;
178
179 // Push the data into three separate vectors, which
180 // we later concatenate. They're kept separate so
181 // if we improve the apt method communication stuff later
182 // it will be better.
183 if (strncmp(buffer, GNUPGBADSIG, sizeof(GNUPGBADSIG)-1) == 0)
184 {
185 if (Debug == true)
186 std::clog << "Got BADSIG! " << std::endl;
187 BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
188 }
189
190 if (strncmp(buffer, GNUPGNOPUBKEY, sizeof(GNUPGNOPUBKEY)-1) == 0)
191 {
192 if (Debug == true)
193 std::clog << "Got NO_PUBKEY " << std::endl;
194 NoPubKeySigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
195 }
196 if (strncmp(buffer, GNUPGNODATA, sizeof(GNUPGBADSIG)-1) == 0)
197 {
198 if (Debug == true)
199 std::clog << "Got NODATA! " << std::endl;
200 BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
201 }
202 if (strncmp(buffer, GNUPGKEYEXPIRED, sizeof(GNUPGKEYEXPIRED)-1) == 0)
203 {
204 if (Debug == true)
205 std::clog << "Got KEYEXPIRED! " << std::endl;
206 WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
207 }
208 if (strncmp(buffer, GNUPGREVKEYSIG, sizeof(GNUPGREVKEYSIG)-1) == 0)
209 {
210 if (Debug == true)
211 std::clog << "Got REVKEYSIG! " << std::endl;
212 WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
213 }
214 if (strncmp(buffer, GNUPGGOODSIG, sizeof(GNUPGGOODSIG)-1) == 0)
215 {
216 char *sig = buffer + sizeof(GNUPGPREFIX);
217 char *p = sig + sizeof("GOODSIG");
218 while (*p && isxdigit(*p))
219 p++;
220 *p = 0;
221 if (Debug == true)
222 std::clog << "Got GOODSIG, key ID:" << sig << std::endl;
223 GoodSigners.push_back(string(sig));
224 }
225 }
226 fclose(pipein);
227
228 waitpid(pid, &status, 0);
229 if (Debug == true)
230 {
231 std::clog << "gpgv exited\n";
232 }
233
234 if (WEXITSTATUS(status) == 0)
235 {
236 if (GoodSigners.empty())
237 return _("Internal error: Good signature, but could not determine key fingerprint?!");
238 return "";
239 }
240 else if (WEXITSTATUS(status) == 1)
241 {
242 return _("At least one invalid signature was encountered.");
243 }
244 else if (WEXITSTATUS(status) == 111)
245 {
246 ioprintf(ret, _("Could not execute '%s' to verify signature (is gpgv installed?)"), gpgvpath.c_str());
247 return ret.str();
248 }
249 else
250 {
251 return _("Unknown error executing gpgv");
252 }
253 }
254
255 bool GPGVMethod::Fetch(FetchItem *Itm)
256 {
257 URI Get = Itm->Uri;
258 string Path = Get.Host + Get.Path; // To account for relative paths
259 string keyID;
260 vector<string> GoodSigners;
261 vector<string> BadSigners;
262 // a worthless signature is a expired or revoked one
263 vector<string> WorthlessSigners;
264 vector<string> NoPubKeySigners;
265
266 FetchResult Res;
267 Res.Filename = Itm->DestFile;
268 URIStart(Res);
269
270 // Run gpgv on file, extract contents and get the key ID of the signer
271 string msg = VerifyGetSigners(Path.c_str(), Itm->DestFile.c_str(),
272 GoodSigners, BadSigners, WorthlessSigners,
273 NoPubKeySigners);
274 if (GoodSigners.empty() || !BadSigners.empty() || !NoPubKeySigners.empty())
275 {
276 string errmsg;
277 // In this case, something bad probably happened, so we just go
278 // with what the other method gave us for an error message.
279 if (BadSigners.empty() && WorthlessSigners.empty() && NoPubKeySigners.empty())
280 errmsg = msg;
281 else
282 {
283 if (!BadSigners.empty())
284 {
285 errmsg += _("The following signatures were invalid:\n");
286 for (vector<string>::iterator I = BadSigners.begin();
287 I != BadSigners.end(); I++)
288 errmsg += (*I + "\n");
289 }
290 if (!WorthlessSigners.empty())
291 {
292 errmsg += _("The following signatures were invalid:\n");
293 for (vector<string>::iterator I = WorthlessSigners.begin();
294 I != WorthlessSigners.end(); I++)
295 errmsg += (*I + "\n");
296 }
297 if (!NoPubKeySigners.empty())
298 {
299 errmsg += _("The following signatures couldn't be verified because the public key is not available:\n");
300 for (vector<string>::iterator I = NoPubKeySigners.begin();
301 I != NoPubKeySigners.end(); I++)
302 errmsg += (*I + "\n");
303 }
304 }
305 // this is only fatal if we have no good sigs or if we have at
306 // least one bad signature. good signatures and NoPubKey signatures
307 // happen easily when a file is signed with multiple signatures
308 if(GoodSigners.empty() or !BadSigners.empty())
309 return _error->Error("%s", errmsg.c_str());
310 }
311
312 // Just pass the raw output up, because passing it as a real data
313 // structure is too difficult with the method stuff. We keep it
314 // as three separate vectors for future extensibility.
315 Res.GPGVOutput = GoodSigners;
316 Res.GPGVOutput.insert(Res.GPGVOutput.end(),BadSigners.begin(),BadSigners.end());
317 Res.GPGVOutput.insert(Res.GPGVOutput.end(),NoPubKeySigners.begin(),NoPubKeySigners.end());
318 URIDone(Res);
319
320 if (_config->FindB("Debug::Acquire::gpgv", false))
321 {
322 std::clog << "gpgv succeeded\n";
323 }
324
325 return true;
326 }
327
328
329 int main()
330 {
331 setlocale(LC_ALL, "");
332
333 GPGVMethod Mth;
334
335 return Mth.Run();
336 }