* apt-pkg/indexcopy.cc:
[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 <apt-pkg/indexcopy.h>
6 #include <apti18n.h>
7
8 #include <utime.h>
9 #include <stdio.h>
10 #include <fcntl.h>
11 #include <errno.h>
12 #include <sys/wait.h>
13 #include <iostream>
14 #include <sstream>
15
16 #include <vector>
17
18 #define GNUPGPREFIX "[GNUPG:]"
19 #define GNUPGBADSIG "[GNUPG:] BADSIG"
20 #define GNUPGNOPUBKEY "[GNUPG:] NO_PUBKEY"
21 #define GNUPGVALIDSIG "[GNUPG:] VALIDSIG"
22 #define GNUPGGOODSIG "[GNUPG:] GOODSIG"
23 #define GNUPGKEYEXPIRED "[GNUPG:] KEYEXPIRED"
24 #define GNUPGREVKEYSIG "[GNUPG:] REVKEYSIG"
25 #define GNUPGNODATA "[GNUPG:] NODATA"
26
27 class GPGVMethod : public pkgAcqMethod
28 {
29 private:
30 string VerifyGetSigners(const char *file, const char *outfile,
31 vector<string> &GoodSigners,
32 vector<string> &BadSigners,
33 vector<string> &WorthlessSigners,
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
44 string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
45 vector<string> &GoodSigners,
46 vector<string> &BadSigners,
47 vector<string> &WorthlessSigners,
48 vector<string> &NoPubKeySigners)
49 {
50 bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
51 // setup a (empty) stringstream for formating the return value
52 std::stringstream ret;
53 ret.str("");
54
55 if (Debug == true)
56 std::clog << "inside VerifyGetSigners" << std::endl;
57
58 pid_t pid;
59 int fd[2];
60 FILE *pipein;
61 int status;
62
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)
66 {
67 // TRANSLATOR: %s is the trusted keyring parts directory
68 ioprintf(ret, _("No keyring installed in %s."),
69 _config->FindDir("Dir::Etc::TrustedParts", "/etc/apt/trusted.gpg.d").c_str());
70 return ret.str();
71 }
72
73 if (pipe(fd) < 0)
74 return "Couldn't create pipe";
75
76 pid = fork();
77 if (pid < 0)
78 return string("Couldn't spawn new process") + strerror(errno);
79 else if (pid == 0)
80 {
81 Args.push_back("--status-fd");
82 Args.push_back("3");
83 Args.push_back(file);
84 Args.push_back(outfile);
85 Args.push_back(NULL);
86
87 if (Debug == true)
88 {
89 std::clog << "Preparing to exec: " << gpgvpath;
90 for(std::vector<const char *>::const_iterator a = Args.begin();*a != NULL; ++a)
91 std::clog << " " << *a;
92 std::clog << std::endl;
93 }
94 int const nullfd = open("/dev/null", O_RDONLY);
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
102 putenv((char *)"LANG=");
103 putenv((char *)"LC_ALL=");
104 putenv((char *)"LC_MESSAGES=");
105 execvp(gpgvpath.c_str(), (char **) &Args[0]);
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;
133 if (Debug == true)
134 std::clog << "Read: " << buffer << std::endl;
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 {
142 if (Debug == true)
143 std::clog << "Got BADSIG! " << std::endl;
144 BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
145 }
146
147 if (strncmp(buffer, GNUPGNOPUBKEY, sizeof(GNUPGNOPUBKEY)-1) == 0)
148 {
149 if (Debug == true)
150 std::clog << "Got NO_PUBKEY " << std::endl;
151 NoPubKeySigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
152 }
153 if (strncmp(buffer, GNUPGNODATA, sizeof(GNUPGBADSIG)-1) == 0)
154 {
155 if (Debug == true)
156 std::clog << "Got NODATA! " << std::endl;
157 BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
158 }
159 if (strncmp(buffer, GNUPGKEYEXPIRED, sizeof(GNUPGKEYEXPIRED)-1) == 0)
160 {
161 if (Debug == true)
162 std::clog << "Got KEYEXPIRED! " << std::endl;
163 WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
164 }
165 if (strncmp(buffer, GNUPGREVKEYSIG, sizeof(GNUPGREVKEYSIG)-1) == 0)
166 {
167 if (Debug == true)
168 std::clog << "Got REVKEYSIG! " << std::endl;
169 WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
170 }
171 if (strncmp(buffer, GNUPGGOODSIG, sizeof(GNUPGGOODSIG)-1) == 0)
172 {
173 char *sig = buffer + sizeof(GNUPGPREFIX);
174 char *p = sig + sizeof("GOODSIG");
175 while (*p && isxdigit(*p))
176 p++;
177 *p = 0;
178 if (Debug == true)
179 std::clog << "Got GOODSIG, key ID:" << sig << std::endl;
180 GoodSigners.push_back(string(sig));
181 }
182 }
183 fclose(pipein);
184
185 waitpid(pid, &status, 0);
186 if (Debug == true)
187 {
188 std::clog << "gpgv exited\n";
189 }
190
191 if (WEXITSTATUS(status) == 0)
192 {
193 if (GoodSigners.empty())
194 return _("Internal error: Good signature, but could not determine key fingerprint?!");
195 return "";
196 }
197 else if (WEXITSTATUS(status) == 1)
198 {
199 return _("At least one invalid signature was encountered.");
200 }
201 else if (WEXITSTATUS(status) == 111)
202 {
203 ioprintf(ret, _("Could not execute '%s' to verify signature (is gpgv installed?)"), gpgvpath.c_str());
204 return ret.str();
205 }
206 else
207 {
208 return _("Unknown error executing gpgv");
209 }
210 }
211
212 bool 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;
219 // a worthless signature is a expired or revoked one
220 vector<string> WorthlessSigners;
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
228 string msg = VerifyGetSigners(Path.c_str(), Itm->DestFile.c_str(),
229 GoodSigners, BadSigners, WorthlessSigners,
230 NoPubKeySigners);
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.
236 if (BadSigners.empty() && WorthlessSigners.empty() && NoPubKeySigners.empty())
237 errmsg = msg;
238 else
239 {
240 if (!BadSigners.empty())
241 {
242 errmsg += _("The following signatures were invalid:\n");
243 for (vector<string>::iterator I = BadSigners.begin();
244 I != BadSigners.end(); I++)
245 errmsg += (*I + "\n");
246 }
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 }
254 if (!NoPubKeySigners.empty())
255 {
256 errmsg += _("The following signatures couldn't be verified because the public key is not available:\n");
257 for (vector<string>::iterator I = NoPubKeySigners.begin();
258 I != NoPubKeySigners.end(); I++)
259 errmsg += (*I + "\n");
260 }
261 }
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())
266 return _error->Error("%s", errmsg.c_str());
267 }
268
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 {
279 std::clog << "gpgv succeeded\n";
280 }
281
282 return true;
283 }
284
285
286 int main()
287 {
288 setlocale(LC_ALL, "");
289
290 GPGVMethod Mth;
291
292 return Mth.Run();
293 }