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