]> git.saurik.com Git - apt.git/blob - methods/gpgv.cc
FildFd: Introduce a Flush() function and call it from Close()
[apt.git] / methods / gpgv.cc
1 #include <config.h>
2
3 #include <apt-pkg/acquire-method.h>
4 #include <apt-pkg/configuration.h>
5 #include <apt-pkg/error.h>
6 #include <apt-pkg/gpgv.h>
7 #include <apt-pkg/strutl.h>
8 #include <apt-pkg/fileutl.h>
9 #include "aptmethod.h"
10
11 #include <ctype.h>
12 #include <errno.h>
13 #include <stddef.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <sys/wait.h>
18 #include <unistd.h>
19
20 #include <algorithm>
21 #include <iostream>
22 #include <string>
23 #include <vector>
24
25 #include <apti18n.h>
26
27 using std::string;
28 using std::vector;
29
30 #define GNUPGPREFIX "[GNUPG:]"
31 #define GNUPGBADSIG "[GNUPG:] BADSIG"
32 #define GNUPGNOPUBKEY "[GNUPG:] NO_PUBKEY"
33 #define GNUPGVALIDSIG "[GNUPG:] VALIDSIG"
34 #define GNUPGGOODSIG "[GNUPG:] GOODSIG"
35 #define GNUPGKEYEXPIRED "[GNUPG:] KEYEXPIRED"
36 #define GNUPGREVKEYSIG "[GNUPG:] REVKEYSIG"
37 #define GNUPGNODATA "[GNUPG:] NODATA"
38
39 class GPGVMethod : public aptMethod
40 {
41 private:
42 string VerifyGetSigners(const char *file, const char *outfile,
43 std::string const &key,
44 vector<string> &GoodSigners,
45 vector<string> &BadSigners,
46 vector<string> &WorthlessSigners,
47 vector<string> &NoPubKeySigners);
48
49 protected:
50 virtual bool URIAcquire(std::string const &Message, FetchItem *Itm) APT_OVERRIDE;
51 public:
52
53 GPGVMethod() : aptMethod("gpgv","1.0",SingleInstance | SendConfig) {};
54 };
55
56 string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
57 std::string const &key,
58 vector<string> &GoodSigners,
59 vector<string> &BadSigners,
60 vector<string> &WorthlessSigners,
61 vector<string> &NoPubKeySigners)
62 {
63 bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
64
65 if (Debug == true)
66 std::clog << "inside VerifyGetSigners" << std::endl;
67
68 int fd[2];
69 bool const keyIsID = (key.empty() == false && key[0] != '/');
70
71 if (pipe(fd) < 0)
72 return "Couldn't create pipe";
73
74 pid_t pid = fork();
75 if (pid < 0)
76 return string("Couldn't spawn new process") + strerror(errno);
77 else if (pid == 0)
78 ExecGPGV(outfile, file, 3, fd, (keyIsID ? "" : key));
79 close(fd[1]);
80
81 FILE *pipein = fdopen(fd[0], "r");
82
83 // Loop over the output of apt-key (which really is gnupg), and check the signatures.
84 std::vector<std::string> ValidSigners;
85 size_t buffersize = 0;
86 char *buffer = NULL;
87 while (1)
88 {
89 if (getline(&buffer, &buffersize, pipein) == -1)
90 break;
91 if (Debug == true)
92 std::clog << "Read: " << buffer << std::endl;
93
94 // Push the data into three separate vectors, which
95 // we later concatenate. They're kept separate so
96 // if we improve the apt method communication stuff later
97 // it will be better.
98 if (strncmp(buffer, GNUPGBADSIG, sizeof(GNUPGBADSIG)-1) == 0)
99 {
100 if (Debug == true)
101 std::clog << "Got BADSIG! " << std::endl;
102 BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
103 }
104 else if (strncmp(buffer, GNUPGNOPUBKEY, sizeof(GNUPGNOPUBKEY)-1) == 0)
105 {
106 if (Debug == true)
107 std::clog << "Got NO_PUBKEY " << std::endl;
108 NoPubKeySigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
109 }
110 else if (strncmp(buffer, GNUPGNODATA, sizeof(GNUPGBADSIG)-1) == 0)
111 {
112 if (Debug == true)
113 std::clog << "Got NODATA! " << std::endl;
114 BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
115 }
116 else if (strncmp(buffer, GNUPGKEYEXPIRED, sizeof(GNUPGKEYEXPIRED)-1) == 0)
117 {
118 if (Debug == true)
119 std::clog << "Got KEYEXPIRED! " << std::endl;
120 WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
121 }
122 else if (strncmp(buffer, GNUPGREVKEYSIG, sizeof(GNUPGREVKEYSIG)-1) == 0)
123 {
124 if (Debug == true)
125 std::clog << "Got REVKEYSIG! " << std::endl;
126 WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
127 }
128 else if (strncmp(buffer, GNUPGGOODSIG, sizeof(GNUPGGOODSIG)-1) == 0)
129 {
130 char *sig = buffer + sizeof(GNUPGPREFIX);
131 char *p = sig + sizeof("GOODSIG");
132 while (*p && isxdigit(*p))
133 p++;
134 *p = 0;
135 if (Debug == true)
136 std::clog << "Got GOODSIG, key ID:" << sig << std::endl;
137 GoodSigners.push_back(string(sig));
138 }
139 else if (strncmp(buffer, GNUPGVALIDSIG, sizeof(GNUPGVALIDSIG)-1) == 0)
140 {
141 char *sig = buffer + sizeof(GNUPGVALIDSIG);
142 char *p = sig;
143 while (*p && isxdigit(*p))
144 p++;
145 *p = 0;
146 if (Debug == true)
147 std::clog << "Got VALIDSIG, key ID: " << sig << std::endl;
148 ValidSigners.push_back(string(sig));
149 }
150 }
151 fclose(pipein);
152 free(buffer);
153
154 // apt-key has a --keyid parameter, but this requires gpg, so we call it without it
155 // and instead check after the fact which keyids where used for verification
156 if (keyIsID == true)
157 {
158 if (Debug == true)
159 std::clog << "GoodSigs needs to be limited to keyid " << key << std::endl;
160 std::vector<std::string>::iterator const foundItr = std::find(ValidSigners.begin(), ValidSigners.end(), key);
161 bool const found = (foundItr != ValidSigners.end());
162 std::copy(GoodSigners.begin(), GoodSigners.end(), std::back_insert_iterator<std::vector<std::string> >(NoPubKeySigners));
163 if (found)
164 {
165 // we look for GOODSIG here as well as an expired sig is a valid sig as well (but not a good one)
166 std::string const goodlongkeyid = "GOODSIG " + key.substr(24, 16);
167 bool const foundGood = std::find(GoodSigners.begin(), GoodSigners.end(), goodlongkeyid) != GoodSigners.end();
168 if (Debug == true)
169 std::clog << "Key " << key << " is valid sig, is " << goodlongkeyid << " also a good one? " << (foundGood ? "yes" : "no") << std::endl;
170 GoodSigners.clear();
171 if (foundGood)
172 {
173 GoodSigners.push_back(goodlongkeyid);
174 NoPubKeySigners.erase(std::remove(NoPubKeySigners.begin(), NoPubKeySigners.end(), goodlongkeyid), NoPubKeySigners.end());
175 }
176 }
177 else
178 GoodSigners.clear();
179 }
180
181 int status;
182 waitpid(pid, &status, 0);
183 if (Debug == true)
184 {
185 ioprintf(std::clog, "gpgv exited with status %i\n", WEXITSTATUS(status));
186 }
187
188 if (WEXITSTATUS(status) == 0)
189 {
190 if (keyIsID)
191 {
192 // gpgv will report success, but we want to enforce a certain keyring
193 // so if we haven't found the key the valid we found is in fact invalid
194 if (GoodSigners.empty())
195 return _("At least one invalid signature was encountered.");
196 }
197 else
198 {
199 if (GoodSigners.empty())
200 return _("Internal error: Good signature, but could not determine key fingerprint?!");
201 }
202 return "";
203 }
204 else if (WEXITSTATUS(status) == 1)
205 return _("At least one invalid signature was encountered.");
206 else if (WEXITSTATUS(status) == 111)
207 return _("Could not execute 'apt-key' to verify signature (is gnupg installed?)");
208 else if (WEXITSTATUS(status) == 112)
209 {
210 // acquire system checks for "NODATA" to generate GPG errors (the others are only warnings)
211 std::string errmsg;
212 //TRANSLATORS: %s is a single techy word like 'NODATA'
213 strprintf(errmsg, _("Clearsigned file isn't valid, got '%s' (does the network require authentication?)"), "NODATA");
214 return errmsg;
215 }
216 else
217 return _("Unknown error executing apt-key");
218 }
219
220 bool GPGVMethod::URIAcquire(std::string const &Message, FetchItem *Itm)
221 {
222 URI const Get = Itm->Uri;
223 string const Path = Get.Host + Get.Path; // To account for relative paths
224 std::string const key = LookupTag(Message, "Signed-By");
225 vector<string> GoodSigners;
226 vector<string> BadSigners;
227 // a worthless signature is a expired or revoked one
228 vector<string> WorthlessSigners;
229 vector<string> NoPubKeySigners;
230
231 FetchResult Res;
232 Res.Filename = Itm->DestFile;
233 URIStart(Res);
234
235 // Run apt-key on file, extract contents and get the key ID of the signer
236 string msg = VerifyGetSigners(Path.c_str(), Itm->DestFile.c_str(), key,
237 GoodSigners, BadSigners, WorthlessSigners,
238 NoPubKeySigners);
239 if (GoodSigners.empty() || !BadSigners.empty() || !NoPubKeySigners.empty())
240 {
241 string errmsg;
242 // In this case, something bad probably happened, so we just go
243 // with what the other method gave us for an error message.
244 if (BadSigners.empty() && WorthlessSigners.empty() && NoPubKeySigners.empty())
245 errmsg = msg;
246 else
247 {
248 if (!BadSigners.empty())
249 {
250 errmsg += _("The following signatures were invalid:\n");
251 for (vector<string>::iterator I = BadSigners.begin();
252 I != BadSigners.end(); ++I)
253 errmsg += (*I + "\n");
254 }
255 if (!WorthlessSigners.empty())
256 {
257 errmsg += _("The following signatures were invalid:\n");
258 for (vector<string>::iterator I = WorthlessSigners.begin();
259 I != WorthlessSigners.end(); ++I)
260 errmsg += (*I + "\n");
261 }
262 if (!NoPubKeySigners.empty())
263 {
264 errmsg += _("The following signatures couldn't be verified because the public key is not available:\n");
265 for (vector<string>::iterator I = NoPubKeySigners.begin();
266 I != NoPubKeySigners.end(); ++I)
267 errmsg += (*I + "\n");
268 }
269 }
270 // this is only fatal if we have no good sigs or if we have at
271 // least one bad signature. good signatures and NoPubKey signatures
272 // happen easily when a file is signed with multiple signatures
273 if(GoodSigners.empty() or !BadSigners.empty())
274 return _error->Error("%s", errmsg.c_str());
275 }
276
277 // Just pass the raw output up, because passing it as a real data
278 // structure is too difficult with the method stuff. We keep it
279 // as three separate vectors for future extensibility.
280 Res.GPGVOutput = GoodSigners;
281 Res.GPGVOutput.insert(Res.GPGVOutput.end(),BadSigners.begin(),BadSigners.end());
282 Res.GPGVOutput.insert(Res.GPGVOutput.end(),NoPubKeySigners.begin(),NoPubKeySigners.end());
283 URIDone(Res);
284
285 if (_config->FindB("Debug::Acquire::gpgv", false))
286 {
287 std::clog << "apt-key succeeded\n";
288 }
289
290 return true;
291 }
292
293
294 int main()
295 {
296 setlocale(LC_ALL, "");
297
298 GPGVMethod Mth;
299
300 return Mth.Run();
301 }