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