]> git.saurik.com Git - apt.git/blob - methods/gpgv.cc
some minor style fixes: use empty() and add a few const
[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 if(i >= 395) {
103 std::clog << _("E: Too many keyrings should be passed to gpgv. Exiting.") << std::endl;
104 exit(111);
105 }
106 }
107
108 Configuration::Item const *Opts;
109 Opts = _config->Tree("Acquire::gpgv::Options");
110 if (Opts != 0)
111 {
112 Opts = Opts->Child;
113 for (; Opts != 0; Opts = Opts->Next)
114 {
115 if (Opts->Value.empty() == true)
116 continue;
117 Args[i++] = Opts->Value.c_str();
118 if(i >= 395) {
119 std::clog << _("E: Argument list from Acquire::gpgv::Options too long. Exiting.") << std::endl;
120 exit(111);
121 }
122 }
123 }
124 Args[i++] = file;
125 Args[i++] = outfile;
126 Args[i++] = NULL;
127
128 if (Debug == true)
129 {
130 std::clog << "Preparing to exec: " << gpgvpath;
131 for(unsigned int j=0;Args[j] != NULL; j++)
132 std::clog << " " << Args[j];
133 std::clog << std::endl;
134 }
135 int const nullfd = open("/dev/null", O_RDONLY);
136 close(fd[0]);
137 // Redirect output to /dev/null; we read from the status fd
138 dup2(nullfd, STDOUT_FILENO);
139 dup2(nullfd, STDERR_FILENO);
140 // Redirect the pipe to the status fd (3)
141 dup2(fd[1], 3);
142
143 putenv((char *)"LANG=");
144 putenv((char *)"LC_ALL=");
145 putenv((char *)"LC_MESSAGES=");
146 execvp(gpgvpath.c_str(), (char **)Args);
147
148 exit(111);
149 }
150 close(fd[1]);
151
152 pipein = fdopen(fd[0], "r");
153
154 // Loop over the output of gpgv, and check the signatures.
155 size_t buffersize = 64;
156 char *buffer = (char *) malloc(buffersize);
157 size_t bufferoff = 0;
158 while (1)
159 {
160 int c;
161
162 // Read a line. Sigh.
163 while ((c = getc(pipein)) != EOF && c != '\n')
164 {
165 if (bufferoff == buffersize)
166 buffer = (char *) realloc(buffer, buffersize *= 2);
167 *(buffer+bufferoff) = c;
168 bufferoff++;
169 }
170 if (bufferoff == 0 && c == EOF)
171 break;
172 *(buffer+bufferoff) = '\0';
173 bufferoff = 0;
174 if (Debug == true)
175 std::clog << "Read: " << buffer << std::endl;
176
177 // Push the data into three separate vectors, which
178 // we later concatenate. They're kept separate so
179 // if we improve the apt method communication stuff later
180 // it will be better.
181 if (strncmp(buffer, GNUPGBADSIG, sizeof(GNUPGBADSIG)-1) == 0)
182 {
183 if (Debug == true)
184 std::clog << "Got BADSIG! " << std::endl;
185 BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
186 }
187
188 if (strncmp(buffer, GNUPGNOPUBKEY, sizeof(GNUPGNOPUBKEY)-1) == 0)
189 {
190 if (Debug == true)
191 std::clog << "Got NO_PUBKEY " << std::endl;
192 NoPubKeySigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
193 }
194 if (strncmp(buffer, GNUPGNODATA, sizeof(GNUPGBADSIG)-1) == 0)
195 {
196 if (Debug == true)
197 std::clog << "Got NODATA! " << std::endl;
198 BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
199 }
200 if (strncmp(buffer, GNUPGKEYEXPIRED, sizeof(GNUPGKEYEXPIRED)-1) == 0)
201 {
202 if (Debug == true)
203 std::clog << "Got KEYEXPIRED! " << std::endl;
204 WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
205 }
206 if (strncmp(buffer, GNUPGREVKEYSIG, sizeof(GNUPGREVKEYSIG)-1) == 0)
207 {
208 if (Debug == true)
209 std::clog << "Got REVKEYSIG! " << std::endl;
210 WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
211 }
212 if (strncmp(buffer, GNUPGGOODSIG, sizeof(GNUPGGOODSIG)-1) == 0)
213 {
214 char *sig = buffer + sizeof(GNUPGPREFIX);
215 char *p = sig + sizeof("GOODSIG");
216 while (*p && isxdigit(*p))
217 p++;
218 *p = 0;
219 if (Debug == true)
220 std::clog << "Got GOODSIG, key ID:" << sig << std::endl;
221 GoodSigners.push_back(string(sig));
222 }
223 }
224 fclose(pipein);
225
226 waitpid(pid, &status, 0);
227 if (Debug == true)
228 {
229 std::clog << "gpgv exited\n";
230 }
231
232 if (WEXITSTATUS(status) == 0)
233 {
234 if (GoodSigners.empty())
235 return _("Internal error: Good signature, but could not determine key fingerprint?!");
236 return "";
237 }
238 else if (WEXITSTATUS(status) == 1)
239 {
240 return _("At least one invalid signature was encountered.");
241 }
242 else if (WEXITSTATUS(status) == 111)
243 {
244 ioprintf(ret, _("Could not execute '%s' to verify signature (is gpgv installed?)"), gpgvpath.c_str());
245 return ret.str();
246 }
247 else
248 {
249 return _("Unknown error executing gpgv");
250 }
251 }
252
253 bool GPGVMethod::Fetch(FetchItem *Itm)
254 {
255 URI Get = Itm->Uri;
256 string Path = Get.Host + Get.Path; // To account for relative paths
257 string keyID;
258 vector<string> GoodSigners;
259 vector<string> BadSigners;
260 // a worthless signature is a expired or revoked one
261 vector<string> WorthlessSigners;
262 vector<string> NoPubKeySigners;
263
264 FetchResult Res;
265 Res.Filename = Itm->DestFile;
266 URIStart(Res);
267
268 // Run gpgv on file, extract contents and get the key ID of the signer
269 string msg = VerifyGetSigners(Path.c_str(), Itm->DestFile.c_str(),
270 GoodSigners, BadSigners, WorthlessSigners,
271 NoPubKeySigners);
272 if (GoodSigners.empty() || !BadSigners.empty() || !NoPubKeySigners.empty())
273 {
274 string errmsg;
275 // In this case, something bad probably happened, so we just go
276 // with what the other method gave us for an error message.
277 if (BadSigners.empty() && WorthlessSigners.empty() && NoPubKeySigners.empty())
278 errmsg = msg;
279 else
280 {
281 if (!BadSigners.empty())
282 {
283 errmsg += _("The following signatures were invalid:\n");
284 for (vector<string>::iterator I = BadSigners.begin();
285 I != BadSigners.end(); I++)
286 errmsg += (*I + "\n");
287 }
288 if (!WorthlessSigners.empty())
289 {
290 errmsg += _("The following signatures were invalid:\n");
291 for (vector<string>::iterator I = WorthlessSigners.begin();
292 I != WorthlessSigners.end(); I++)
293 errmsg += (*I + "\n");
294 }
295 if (!NoPubKeySigners.empty())
296 {
297 errmsg += _("The following signatures couldn't be verified because the public key is not available:\n");
298 for (vector<string>::iterator I = NoPubKeySigners.begin();
299 I != NoPubKeySigners.end(); I++)
300 errmsg += (*I + "\n");
301 }
302 }
303 // this is only fatal if we have no good sigs or if we have at
304 // least one bad signature. good signatures and NoPubKey signatures
305 // happen easily when a file is signed with multiple signatures
306 if(GoodSigners.empty() or !BadSigners.empty())
307 return _error->Error("%s", errmsg.c_str());
308 }
309
310 // Just pass the raw output up, because passing it as a real data
311 // structure is too difficult with the method stuff. We keep it
312 // as three separate vectors for future extensibility.
313 Res.GPGVOutput = GoodSigners;
314 Res.GPGVOutput.insert(Res.GPGVOutput.end(),BadSigners.begin(),BadSigners.end());
315 Res.GPGVOutput.insert(Res.GPGVOutput.end(),NoPubKeySigners.begin(),NoPubKeySigners.end());
316 URIDone(Res);
317
318 if (_config->FindB("Debug::Acquire::gpgv", false))
319 {
320 std::clog << "gpgv succeeded\n";
321 }
322
323 return true;
324 }
325
326
327 int main()
328 {
329 setlocale(LC_ALL, "");
330
331 GPGVMethod Mth;
332
333 return Mth.Run();
334 }