* French manpage translation update
[ntk/apt.git] / methods / gpgv.cc
CommitLineData
b3d44315
MV
1#include <apt-pkg/error.h>
2#include <apt-pkg/acquire-method.h>
3#include <apt-pkg/strutl.h>
46e39c8e 4#include <apt-pkg/fileutl.h>
339690e4 5#include <apti18n.h>
b3d44315 6
b3d44315
MV
7#include <utime.h>
8#include <stdio.h>
9#include <fcntl.h>
10#include <errno.h>
11#include <sys/wait.h>
12#include <iostream>
da9ed163 13#include <sstream>
b3d44315
MV
14
15#define GNUPGPREFIX "[GNUPG:]"
16#define GNUPGBADSIG "[GNUPG:] BADSIG"
17#define GNUPGNOPUBKEY "[GNUPG:] NO_PUBKEY"
18#define GNUPGVALIDSIG "[GNUPG:] VALIDSIG"
c5d8878d
MV
19#define GNUPGGOODSIG "[GNUPG:] GOODSIG"
20#define GNUPGKEYEXPIRED "[GNUPG:] KEYEXPIRED"
21#define GNUPGREVKEYSIG "[GNUPG:] REVKEYSIG"
2abb68b7 22#define GNUPGNODATA "[GNUPG:] NODATA"
b3d44315
MV
23
24class GPGVMethod : public pkgAcqMethod
25{
26 private:
da9ed163 27 string VerifyGetSigners(const char *file, const char *outfile,
c5d8878d
MV
28 vector<string> &GoodSigners,
29 vector<string> &BadSigners,
30 vector<string> &WorthlessSigners,
b3d44315
MV
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
da9ed163 41string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
b3d44315
MV
42 vector<string> &GoodSigners,
43 vector<string> &BadSigners,
c5d8878d 44 vector<string> &WorthlessSigners,
b3d44315
MV
45 vector<string> &NoPubKeySigners)
46{
46e39c8e 47 bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
da9ed163
MV
48 // setup a (empty) stringstream for formating the return value
49 std::stringstream ret;
b2c22075 50 ret.str("");
da9ed163 51
46e39c8e
MV
52 if (Debug == true)
53 std::clog << "inside VerifyGetSigners" << std::endl;
54
b3d44315
MV
55 pid_t pid;
56 int fd[2];
57 FILE *pipein;
58 int status;
46e39c8e
MV
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)
b3d44315 65 {
46e39c8e
MV
66 std::clog << "gpgv path: " << gpgvpath << std::endl;
67 std::clog << "Keyring file: " << trustedFile << std::endl;
68 std::clog << "Keyring path: " << trustedPath << std::endl;
b3d44315
MV
69 }
70
46e39c8e
MV
71 vector<string> keyrings = GetListOfFilesInDir(trustedPath, "gpg", false);
72 if (FileExists(trustedFile) == true)
73 keyrings.push_back(trustedFile);
74
75 if (keyrings.empty() == true)
da9ed163 76 {
46e39c8e
MV
77 // TRANSLATOR: %s is the trusted keyring parts directory
78 ioprintf(ret, _("No keyring installed in %s."), trustedPath.c_str());
da9ed163
MV
79 return ret.str();
80 }
46e39c8e 81
b3d44315 82 if (pipe(fd) < 0)
b3d44315 83 return "Couldn't create pipe";
b3d44315
MV
84
85 pid = fork();
86 if (pid < 0)
da9ed163 87 return string("Couldn't spawn new process") + strerror(errno);
b3d44315
MV
88 else if (pid == 0)
89 {
ad6a45d8
MV
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";
9129f2af 96 Args[i++] = "--ignore-time-conflict";
46e39c8e
MV
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 }
ad6a45d8
MV
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();
46e39c8e
MV
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;
ad6a45d8
MV
122 exit(111);
123 }
124 }
125 }
126 Args[i++] = file;
127 Args[i++] = outfile;
128 Args[i++] = NULL;
129
46e39c8e 130 if (Debug == true)
b3d44315 131 {
46e39c8e 132 std::clog << "Preparing to exec: " << gpgvpath;
ec048f63 133 for(unsigned int j=0;Args[j] != NULL; j++)
46e39c8e
MV
134 std::clog << " " << Args[j];
135 std::clog << std::endl;
b3d44315 136 }
46e39c8e 137 int const nullfd = open("/dev/null", O_RDONLY);
b3d44315
MV
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
31c64df3
OS
145 putenv((char *)"LANG=");
146 putenv((char *)"LC_ALL=");
147 putenv((char *)"LC_MESSAGES=");
ad6a45d8 148 execvp(gpgvpath.c_str(), (char **)Args);
b3d44315
MV
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;
46e39c8e
MV
176 if (Debug == true)
177 std::clog << "Read: " << buffer << std::endl;
b3d44315
MV
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 {
46e39c8e
MV
185 if (Debug == true)
186 std::clog << "Got BADSIG! " << std::endl;
b3d44315
MV
187 BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
188 }
189
190 if (strncmp(buffer, GNUPGNOPUBKEY, sizeof(GNUPGNOPUBKEY)-1) == 0)
191 {
46e39c8e
MV
192 if (Debug == true)
193 std::clog << "Got NO_PUBKEY " << std::endl;
b3d44315
MV
194 NoPubKeySigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
195 }
2abb68b7
MV
196 if (strncmp(buffer, GNUPGNODATA, sizeof(GNUPGBADSIG)-1) == 0)
197 {
46e39c8e
MV
198 if (Debug == true)
199 std::clog << "Got NODATA! " << std::endl;
2abb68b7
MV
200 BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
201 }
c5d8878d
MV
202 if (strncmp(buffer, GNUPGKEYEXPIRED, sizeof(GNUPGKEYEXPIRED)-1) == 0)
203 {
46e39c8e
MV
204 if (Debug == true)
205 std::clog << "Got KEYEXPIRED! " << std::endl;
c5d8878d
MV
206 WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
207 }
208 if (strncmp(buffer, GNUPGREVKEYSIG, sizeof(GNUPGREVKEYSIG)-1) == 0)
209 {
46e39c8e
MV
210 if (Debug == true)
211 std::clog << "Got REVKEYSIG! " << std::endl;
c5d8878d
MV
212 WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
213 }
214 if (strncmp(buffer, GNUPGGOODSIG, sizeof(GNUPGGOODSIG)-1) == 0)
b3d44315
MV
215 {
216 char *sig = buffer + sizeof(GNUPGPREFIX);
c5d8878d 217 char *p = sig + sizeof("GOODSIG");
b3d44315
MV
218 while (*p && isxdigit(*p))
219 p++;
220 *p = 0;
46e39c8e
MV
221 if (Debug == true)
222 std::clog << "Got GOODSIG, key ID:" << sig << std::endl;
b3d44315
MV
223 GoodSigners.push_back(string(sig));
224 }
225 }
226 fclose(pipein);
227
228 waitpid(pid, &status, 0);
46e39c8e 229 if (Debug == true)
b3d44315 230 {
46e39c8e 231 std::clog << "gpgv exited\n";
b3d44315
MV
232 }
233
234 if (WEXITSTATUS(status) == 0)
235 {
236 if (GoodSigners.empty())
339690e4 237 return _("Internal error: Good signature, but could not determine key fingerprint?!");
da9ed163 238 return "";
b3d44315
MV
239 }
240 else if (WEXITSTATUS(status) == 1)
241 {
339690e4 242 return _("At least one invalid signature was encountered.");
b3d44315
MV
243 }
244 else if (WEXITSTATUS(status) == 111)
245 {
f83589b5 246 ioprintf(ret, _("Could not execute '%s' to verify signature (is gpgv installed?)"), gpgvpath.c_str());
da9ed163 247 return ret.str();
b3d44315
MV
248 }
249 else
250 {
339690e4 251 return _("Unknown error executing gpgv");
b3d44315
MV
252 }
253}
254
255bool 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;
c5d8878d
MV
262 // a worthless signature is a expired or revoked one
263 vector<string> WorthlessSigners;
b3d44315
MV
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
da9ed163 271 string msg = VerifyGetSigners(Path.c_str(), Itm->DestFile.c_str(),
c5d8878d
MV
272 GoodSigners, BadSigners, WorthlessSigners,
273 NoPubKeySigners);
b3d44315
MV
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.
c5d8878d 279 if (BadSigners.empty() && WorthlessSigners.empty() && NoPubKeySigners.empty())
b3d44315
MV
280 errmsg = msg;
281 else
282 {
283 if (!BadSigners.empty())
284 {
339690e4 285 errmsg += _("The following signatures were invalid:\n");
b3d44315
MV
286 for (vector<string>::iterator I = BadSigners.begin();
287 I != BadSigners.end(); I++)
288 errmsg += (*I + "\n");
289 }
c5d8878d
MV
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 }
b3d44315
MV
297 if (!NoPubKeySigners.empty())
298 {
339690e4 299 errmsg += _("The following signatures couldn't be verified because the public key is not available:\n");
b3d44315
MV
300 for (vector<string>::iterator I = NoPubKeySigners.begin();
301 I != NoPubKeySigners.end(); I++)
302 errmsg += (*I + "\n");
303 }
304 }
ce424cd4
MV
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())
f23153d0 309 return _error->Error("%s", errmsg.c_str());
b3d44315
MV
310 }
311
b3d44315
MV
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 {
46e39c8e 322 std::clog << "gpgv succeeded\n";
b3d44315
MV
323 }
324
325 return true;
326}
327
328
329int main()
330{
339690e4
MV
331 setlocale(LC_ALL, "");
332
b3d44315
MV
333 GPGVMethod Mth;
334
335 return Mth.Run();
336}