]> git.proxmox.com Git - cargo.git/blob - debian/patches/cve/CVE-2022-46176-01-validate-ssh-host.keys.patch
590dd5d97d13386206dd625edd24414af06db81a
[cargo.git] / debian / patches / cve / CVE-2022-46176-01-validate-ssh-host.keys.patch
1 This patch is based on the upstream commit described below, adapted for use
2 in the Debian package by Peter Michael Green.
3
4 commit 1387fd4105b242fa2d24ad99d10a5b1af23f293e
5 Author: Eric Huss <eric@huss.org>
6 Date: Wed Dec 7 18:52:00 2022 -0800
7
8 Validate SSH host keys
9
10 Index: cargo/src/cargo/sources/git/known_hosts.rs
11 ===================================================================
12 --- /dev/null
13 +++ cargo/src/cargo/sources/git/known_hosts.rs
14 @@ -0,0 +1,439 @@
15 +//! SSH host key validation support.
16 +//!
17 +//! A primary goal with this implementation is to provide user-friendly error
18 +//! messages, guiding them to understand the issue and how to resolve it.
19 +//!
20 +//! Note that there are a lot of limitations here. This reads OpenSSH
21 +//! known_hosts files from well-known locations, but it does not read OpenSSH
22 +//! config files. The config file can change the behavior of how OpenSSH
23 +//! handles known_hosts files. For example, some things we don't handle:
24 +//!
25 +//! - `GlobalKnownHostsFile` — Changes the location of the global host file.
26 +//! - `UserKnownHostsFile` — Changes the location of the user's host file.
27 +//! - `KnownHostsCommand` — A command to fetch known hosts.
28 +//! - `CheckHostIP` — DNS spoofing checks.
29 +//! - `VisualHostKey` — Shows a visual ascii-art key.
30 +//! - `VerifyHostKeyDNS` — Uses SSHFP DNS records to fetch a host key.
31 +//!
32 +//! There's also a number of things that aren't supported but could be easily
33 +//! added (it just adds a little complexity). For example, hashed hostnames,
34 +//! hostname patterns, and revoked markers. See "FIXME" comments littered in
35 +//! this file.
36 +
37 +use git2::cert::Cert;
38 +use git2::CertificateCheckStatus;
39 +use std::collections::HashSet;
40 +use std::fmt::Write;
41 +use std::path::{Path, PathBuf};
42 +
43 +/// These are host keys that are hard-coded in cargo to provide convenience.
44 +///
45 +/// If GitHub ever publishes new keys, the user can add them to their own
46 +/// configuration file to use those instead.
47 +///
48 +/// The GitHub keys are sourced from <https://api.github.com/meta> or
49 +/// <https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints>.
50 +///
51 +/// These will be ignored if the user adds their own entries for `github.com`,
52 +/// which can be useful if GitHub ever revokes their old keys.
53 +static BUNDLED_KEYS: &[(&str, &str, &str)] = &[
54 + ("github.com", "ssh-ed25519", "AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl"),
55 + ("github.com", "ecdsa-sha2-nistp256", "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg="),
56 + ("github.com", "ssh-rsa", "AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ=="),
57 +];
58 +
59 +enum KnownHostError {
60 + /// Some general error happened while validating the known hosts.
61 + CheckError(anyhow::Error),
62 + /// The host key was not found.
63 + HostKeyNotFound {
64 + hostname: String,
65 + key_type: git2::cert::SshHostKeyType,
66 + remote_host_key: String,
67 + remote_fingerprint: String,
68 + other_hosts: Vec<KnownHost>,
69 + },
70 + /// The host key was found, but does not match the remote's key.
71 + HostKeyHasChanged {
72 + hostname: String,
73 + key_type: git2::cert::SshHostKeyType,
74 + old_known_host: KnownHost,
75 + remote_host_key: String,
76 + remote_fingerprint: String,
77 + },
78 +}
79 +
80 +impl From<anyhow::Error> for KnownHostError {
81 + fn from(err: anyhow::Error) -> KnownHostError {
82 + KnownHostError::CheckError(err.into())
83 + }
84 +}
85 +
86 +/// The location where a host key was located.
87 +#[derive(Clone)]
88 +enum KnownHostLocation {
89 + /// Loaded from a file from disk.
90 + File { path: PathBuf, lineno: u32 },
91 + /// Part of the hard-coded bundled keys in Cargo.
92 + Bundled,
93 +}
94 +
95 +/// The git2 callback used to validate a certificate (only ssh known hosts are validated).
96 +pub fn certificate_check(
97 + cert: &Cert<'_>,
98 + host: &str,
99 + port: Option<u16>,
100 +) -> Result<CertificateCheckStatus, git2::Error> {
101 + let Some(host_key) = cert.as_hostkey() else {
102 + // Return passthrough for TLS X509 certificates to use whatever validation
103 + // was done in git2.
104 + return Ok(CertificateCheckStatus::CertificatePassthrough)
105 + };
106 + // If a nonstandard port is in use, check for that first.
107 + // The fallback to check without a port is handled in the HostKeyNotFound handler.
108 + let host_maybe_port = match port {
109 + Some(port) if port != 22 => format!("[{host}]:{port}"),
110 + _ => host.to_string(),
111 + };
112 + // The error message must be constructed as a string to pass through the libgit2 C API.
113 + let err_msg = match check_ssh_known_hosts(host_key, &host_maybe_port) {
114 + Ok(()) => {
115 + return Ok(CertificateCheckStatus::CertificateOk);
116 + }
117 + Err(KnownHostError::CheckError(e)) => {
118 + format!("error: failed to validate host key:\n{:#}", e)
119 + }
120 + Err(KnownHostError::HostKeyNotFound {
121 + hostname,
122 + key_type,
123 + remote_host_key,
124 + remote_fingerprint,
125 + other_hosts,
126 + }) => {
127 + // Try checking without the port.
128 + if port.is_some()
129 + && !matches!(port, Some(22))
130 + && check_ssh_known_hosts(host_key, host).is_ok()
131 + {
132 + return Ok(CertificateCheckStatus::CertificateOk);
133 + }
134 + let key_type_short_name = key_type.short_name();
135 + let key_type_name = key_type.name();
136 + let known_hosts_location = user_known_host_location_to_add();
137 + let other_hosts_message = if other_hosts.is_empty() {
138 + String::new()
139 + } else {
140 + let mut msg = String::from(
141 + "Note: This host key was found, \
142 + but is associated with a different host:\n",
143 + );
144 + for known_host in other_hosts {
145 + let loc = match known_host.location {
146 + KnownHostLocation::File { path, lineno } => {
147 + format!("{} line {lineno}", path.display())
148 + }
149 + KnownHostLocation::Bundled => format!("bundled with cargo"),
150 + };
151 + write!(msg, " {loc}: {}\n", known_host.patterns).unwrap();
152 + }
153 + msg
154 + };
155 + format!("error: unknown SSH host key\n\
156 + The SSH host key for `{hostname}` is not known and cannot be validated.\n\
157 + \n\
158 + To resolve this issue, add the host key to {known_hosts_location}\n\
159 + \n\
160 + The key to add is:\n\
161 + \n\
162 + {hostname} {key_type_name} {remote_host_key}\n\
163 + \n\
164 + The {key_type_short_name} key fingerprint is: SHA256:{remote_fingerprint}\n\
165 + This fingerprint should be validated with the server administrator that it is correct.\n\
166 + {other_hosts_message}\n\
167 + See https://doc.rust-lang.org/nightly/cargo/appendix/git-authentication.html#ssh-known-hosts \
168 + for more information.\n\
169 + ")
170 + }
171 + Err(KnownHostError::HostKeyHasChanged {
172 + hostname,
173 + key_type,
174 + old_known_host,
175 + remote_host_key,
176 + remote_fingerprint,
177 + }) => {
178 + let key_type_short_name = key_type.short_name();
179 + let key_type_name = key_type.name();
180 + let known_hosts_location = user_known_host_location_to_add();
181 + let old_key_resolution = match old_known_host.location {
182 + KnownHostLocation::File { path, lineno } => {
183 + let old_key_location = path.display();
184 + format!(
185 + "removing the old {key_type_name} key for `{hostname}` \
186 + located at {old_key_location} line {lineno}, \
187 + and adding the new key to {known_hosts_location}",
188 + )
189 + }
190 + KnownHostLocation::Bundled => {
191 + format!(
192 + "adding the new key to {known_hosts_location}\n\
193 + The current host key is bundled as part of Cargo."
194 + )
195 + }
196 + };
197 + format!("error: SSH host key has changed for `{hostname}`\n\
198 + *********************************\n\
199 + * WARNING: HOST KEY HAS CHANGED *\n\
200 + *********************************\n\
201 + This may be caused by a man-in-the-middle attack, or the \
202 + server may have changed its host key.\n\
203 + \n\
204 + The {key_type_short_name} fingerprint for the key from the remote host is:\n\
205 + SHA256:{remote_fingerprint}\n\
206 + \n\
207 + You are strongly encouraged to contact the server \
208 + administrator for `{hostname}` to verify that this new key is \
209 + correct.\n\
210 + \n\
211 + If you can verify that the server has a new key, you can \
212 + resolve this error by {old_key_resolution}\n\
213 + \n\
214 + The key provided by the remote host is:\n\
215 + \n\
216 + {hostname} {key_type_name} {remote_host_key}\n\
217 + \n\
218 + See https://doc.rust-lang.org/nightly/cargo/appendix/git-authentication.html#ssh-known-hosts \
219 + for more information.\n\
220 + ")
221 + }
222 + };
223 + Err(git2::Error::new(
224 + git2::ErrorCode::GenericError,
225 + git2::ErrorClass::Callback,
226 + err_msg,
227 + ))
228 +}
229 +
230 +/// Checks if the given host/host key pair is known.
231 +fn check_ssh_known_hosts(
232 + cert_host_key: &git2::cert::CertHostkey<'_>,
233 + host: &str,
234 +) -> Result<(), KnownHostError> {
235 + let Some(remote_host_key) = cert_host_key.hostkey() else {
236 + return Err(anyhow::format_err!("remote host key is not available").into());
237 + };
238 + let remote_key_type = cert_host_key.hostkey_type().unwrap();
239 + // `changed_key` keeps track of any entries where the key has changed.
240 + let mut changed_key = None;
241 + // `other_hosts` keeps track of any entries that have an identical key,
242 + // but a different hostname.
243 + let mut other_hosts = Vec::new();
244 +
245 + // Collect all the known host entries from disk.
246 + let mut known_hosts = Vec::new();
247 + for path in known_host_files() {
248 + if !path.exists() {
249 + continue;
250 + }
251 + let hosts = load_hostfile(&path)?;
252 + known_hosts.extend(hosts);
253 + }
254 + // Load the bundled keys. Don't add keys for hosts that the user has
255 + // configured, which gives them the option to override them. This could be
256 + // useful if the keys are ever revoked.
257 + let configured_hosts: HashSet<_> = known_hosts
258 + .iter()
259 + .flat_map(|known_host| {
260 + known_host
261 + .patterns
262 + .split(',')
263 + .map(|pattern| pattern.to_lowercase())
264 + })
265 + .collect();
266 + for (patterns, key_type, key) in BUNDLED_KEYS {
267 + if !configured_hosts.contains(*patterns) {
268 + let key = base64::decode(key).unwrap();
269 + known_hosts.push(KnownHost {
270 + location: KnownHostLocation::Bundled,
271 + patterns: patterns.to_string(),
272 + key_type: key_type.to_string(),
273 + key,
274 + });
275 + }
276 + }
277 +
278 + for known_host in known_hosts {
279 + // The key type from libgit2 needs to match the key type from the host file.
280 + if known_host.key_type != remote_key_type.name() {
281 + continue;
282 + }
283 + let key_matches = known_host.key == remote_host_key;
284 + if !known_host.host_matches(host) {
285 + // `name` can be None for hashed hostnames (which libgit2 does not expose).
286 + if key_matches {
287 + other_hosts.push(known_host.clone());
288 + }
289 + continue;
290 + }
291 + if key_matches {
292 + return Ok(());
293 + }
294 + // The host and key type matched, but the key itself did not.
295 + // This indicates the key has changed.
296 + // This is only reported as an error if no subsequent lines have a
297 + // correct key.
298 + changed_key = Some(known_host.clone());
299 + }
300 + // Older versions of OpenSSH (before 6.8, March 2015) showed MD5
301 + // fingerprints (see FingerprintHash ssh config option). Here we only
302 + // support SHA256.
303 + let mut remote_fingerprint = cargo_util::Sha256::new();
304 + remote_fingerprint.update(remote_host_key);
305 + let remote_fingerprint =
306 + base64::encode_config(remote_fingerprint.finish(), base64::STANDARD_NO_PAD);
307 + let remote_host_key = base64::encode(remote_host_key);
308 + // FIXME: Ideally the error message should include the IP address of the
309 + // remote host (to help the user validate that they are connecting to the
310 + // host they were expecting to). However, I don't see a way to obtain that
311 + // information from libgit2.
312 + match changed_key {
313 + Some(old_known_host) => Err(KnownHostError::HostKeyHasChanged {
314 + hostname: host.to_string(),
315 + key_type: remote_key_type,
316 + old_known_host,
317 + remote_host_key,
318 + remote_fingerprint,
319 + }),
320 + None => Err(KnownHostError::HostKeyNotFound {
321 + hostname: host.to_string(),
322 + key_type: remote_key_type,
323 + remote_host_key,
324 + remote_fingerprint,
325 + other_hosts,
326 + }),
327 + }
328 +}
329 +
330 +/// Returns a list of files to try loading OpenSSH-formatted known hosts.
331 +fn known_host_files() -> Vec<PathBuf> {
332 + let mut result = Vec::new();
333 + if cfg!(unix) {
334 + result.push(PathBuf::from("/etc/ssh/ssh_known_hosts"));
335 + } else if cfg!(windows) {
336 + // The msys/cygwin version of OpenSSH uses `/etc` from the posix root
337 + // filesystem there (such as `C:\msys64\etc\ssh\ssh_known_hosts`).
338 + // However, I do not know of a way to obtain that location from
339 + // Windows-land. The ProgramData version here is what the PowerShell
340 + // port of OpenSSH does.
341 + if let Some(progdata) = std::env::var_os("ProgramData") {
342 + let mut progdata = PathBuf::from(progdata);
343 + progdata.push("ssh");
344 + progdata.push("ssh_known_hosts");
345 + result.push(progdata)
346 + }
347 + }
348 + result.extend(user_known_host_location());
349 + result
350 +}
351 +
352 +/// The location of the user's known_hosts file.
353 +fn user_known_host_location() -> Option<PathBuf> {
354 + // NOTE: This is a potentially inaccurate prediction of what the user
355 + // actually wants. The actual location depends on several factors:
356 + //
357 + // - Windows OpenSSH Powershell version: I believe this looks up the home
358 + // directory via ProfileImagePath in the registry, falling back to
359 + // `GetWindowsDirectoryW` if that fails.
360 + // - OpenSSH Portable (under msys): This is very complicated. I got lost
361 + // after following it through some ldap/active directory stuff.
362 + // - OpenSSH (most unix platforms): Uses `pw->pw_dir` from `getpwuid()`.
363 + //
364 + // This doesn't do anything close to that. home_dir's behavior is:
365 + // - Windows: $USERPROFILE, or SHGetFolderPathW()
366 + // - Unix: $HOME, or getpwuid_r()
367 + //
368 + // Since there is a mismatch here, the location returned here might be
369 + // different than what the user's `ssh` CLI command uses. We may want to
370 + // consider trying to align it better.
371 + home::home_dir().map(|mut home| {
372 + home.push(".ssh");
373 + home.push("known_hosts");
374 + home
375 + })
376 +}
377 +
378 +/// The location to display in an error message instructing the user where to
379 +/// add the new key.
380 +fn user_known_host_location_to_add() -> String {
381 + // Note that we don't bother with the legacy known_hosts2 files.
382 + match user_known_host_location() {
383 + Some(path) => path.to_str().expect("utf-8 home").to_string(),
384 + None => "~/.ssh/known_hosts".to_string(),
385 + }
386 +}
387 +
388 +/// A single known host entry.
389 +#[derive(Clone)]
390 +struct KnownHost {
391 + location: KnownHostLocation,
392 + /// The hostname. May be comma separated to match multiple hosts.
393 + patterns: String,
394 + key_type: String,
395 + key: Vec<u8>,
396 +}
397 +
398 +impl KnownHost {
399 + /// Returns whether or not the given host matches this known host entry.
400 + fn host_matches(&self, host: &str) -> bool {
401 + let mut match_found = false;
402 + let host = host.to_lowercase();
403 + // FIXME: support hashed hostnames
404 + for pattern in self.patterns.split(',') {
405 + let pattern = pattern.to_lowercase();
406 + // FIXME: support * and ? wildcards
407 + if let Some(pattern) = pattern.strip_prefix('!') {
408 + if pattern == host {
409 + return false;
410 + }
411 + } else {
412 + match_found = pattern == host;
413 + }
414 + }
415 + match_found
416 + }
417 +}
418 +
419 +/// Loads an OpenSSH known_hosts file.
420 +fn load_hostfile(path: &Path) -> Result<Vec<KnownHost>, anyhow::Error> {
421 + let contents = cargo_util::paths::read(path)?;
422 + let entries = contents
423 + .lines()
424 + .enumerate()
425 + .filter_map(|(lineno, line)| {
426 + let location = KnownHostLocation::File {
427 + path: path.to_path_buf(),
428 + lineno: lineno as u32 + 1,
429 + };
430 + parse_known_hosts_line(line, location)
431 + })
432 + .collect();
433 + Ok(entries)
434 +}
435 +
436 +fn parse_known_hosts_line(line: &str, location: KnownHostLocation) -> Option<KnownHost> {
437 + let line = line.trim();
438 + // FIXME: @revoked and @cert-authority is currently not supported.
439 + if line.is_empty() || line.starts_with('#') || line.starts_with('@') {
440 + return None;
441 + }
442 + let mut parts = line.split([' ', '\t']).filter(|s| !s.is_empty());
443 + let Some(patterns) = parts.next() else { return None };
444 + let Some(key_type) = parts.next() else { return None };
445 + let Some(key) = parts.next() else { return None };
446 + let Ok(key) = base64::decode(key) else { return None };
447 + Some(KnownHost {
448 + location,
449 + patterns: patterns.to_string(),
450 + key_type: key_type.to_string(),
451 + key,
452 + })
453 +}
454 Index: cargo/src/cargo/sources/git/mod.rs
455 ===================================================================
456 --- cargo.orig/src/cargo/sources/git/mod.rs
457 +++ cargo/src/cargo/sources/git/mod.rs
458 @@ -1,4 +1,5 @@
459 pub use self::source::GitSource;
460 pub use self::utils::{fetch, GitCheckout, GitDatabase, GitRemote};
461 +mod known_hosts;
462 mod source;
463 mod utils;
464 Index: cargo/src/cargo/sources/git/utils.rs
465 ===================================================================
466 --- cargo.orig/src/cargo/sources/git/utils.rs
467 +++ cargo/src/cargo/sources/git/utils.rs
468 @@ -647,7 +647,6 @@ where
469 | ErrorClass::Submodule
470 | ErrorClass::FetchHead
471 | ErrorClass::Ssh
472 - | ErrorClass::Callback
473 | ErrorClass::Http => {
474 let mut msg = "network failure seems to have happened\n".to_string();
475 msg.push_str(
476 @@ -658,6 +657,13 @@ where
477 );
478 err = err.context(msg);
479 }
480 + ErrorClass::Callback => {
481 + // This unwraps the git2 error. We're using the callback error
482 + // specifically to convey errors from Rust land through the C
483 + // callback interface. We don't need the `; class=Callback
484 + // (26)` that gets tacked on to the git2 error message.
485 + err = anyhow::format_err!("{}", e.message());
486 + }
487 _ => {}
488 }
489 }
490 @@ -686,12 +692,16 @@ pub fn with_fetch_options(
491 let mut progress = Progress::new("Fetch", config);
492 network::with_retry(config, || {
493 with_authentication(url, git_config, |f| {
494 + let port = Url::parse(url).ok().and_then(|url| url.port());
495 let mut last_update = Instant::now();
496 let mut rcb = git2::RemoteCallbacks::new();
497 // We choose `N=10` here to make a `300ms * 10slots ~= 3000ms`
498 // sliding window for tracking the data transfer rate (in bytes/s).
499 let mut counter = MetricsCounter::<10>::new(0, last_update);
500 rcb.credentials(f);
501 + rcb.certificate_check(|cert, host| {
502 + super::known_hosts::certificate_check(cert, host, port)
503 + });
504 rcb.transfer_progress(|stats| {
505 let indexed_deltas = stats.indexed_deltas();
506 let msg = if indexed_deltas > 0 {
507 Index: cargo/src/doc/src/appendix/git-authentication.md
508 ===================================================================
509 --- cargo.orig/src/doc/src/appendix/git-authentication.md
510 +++ cargo/src/doc/src/appendix/git-authentication.md
511 @@ -58,9 +58,32 @@ on how to start `ssh-agent` and to add k
512 > used by Cargo's built-in SSH library. More advanced requirements should use
513 > [`net.git-fetch-with-cli`].
514
515 +### SSH Known Hosts
516 +
517 +When connecting to an SSH host, Cargo must verify the identity of the host
518 +using "known hosts", which are a list of host keys. Cargo can look for these
519 +known hosts in OpenSSH-style `known_hosts` files located in their standard
520 +locations (`.ssh/known_hosts` in your home directory, or
521 +`/etc/ssh/ssh_known_hosts` on Unix-like platforms or
522 +`%PROGRAMDATA%\ssh\ssh_known_hosts` on Windows). More information about these
523 +files can be found in the [sshd man page].
524 +
525 +When connecting to an SSH host before the known hosts has been configured,
526 +Cargo will display an error message instructing you how to add the host key.
527 +This also includes a "fingerprint", which is a smaller hash of the host key,
528 +which should be easier to visually verify. The server administrator can get
529 +the fingerprint by running `ssh-keygen` against the public key (for example,
530 +`ssh-keygen -l -f /etc/ssh/ssh_host_ecdsa_key.pub`). Well-known sites may
531 +publish their fingerprints on the web; for example GitHub posts theirs at
532 +<https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints>.
533 +
534 +Cargo comes with the host keys for [github.com](https://github.com) built-in.
535 +If those ever change, you can add the new keys to your known_hosts file.
536 +
537 [`credential.helper`]: https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage
538 [`net.git-fetch-with-cli`]: ../reference/config.md#netgit-fetch-with-cli
539 [GCM]: https://github.com/microsoft/Git-Credential-Manager-Core/
540 [PuTTY]: https://www.chiark.greenend.org.uk/~sgtatham/putty/
541 [Microsoft installation documentation]: https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse
542 [key management]: https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_keymanagement
543 +[sshd man page]: https://man.openbsd.org/sshd#SSH_KNOWN_HOSTS_FILE_FORMAT
544 --- rust-cargo-0.66.0.orig/Cargo.toml
545 +++ rust-cargo-0.66.0/Cargo.toml
546 @@ -17,6 +17,7 @@ path = "src/cargo/lib.rs"
547
548 [dependencies]
549 atty = "0.2"
550 +base64 = "0.13"
551 bytesize = "1.0"
552 cargo-platform = { path = "crates/cargo-platform", version = "0.1.2" }
553 cargo-util = { path = "crates/cargo-util", version = "0.2.1" }
554 @@ -28,8 +29,8 @@ pretty_env_logger = { version = "0.4", o
555 anyhow = "1.0"
556 filetime = "0.2.9"
557 flate2 = { version = "1.0.3", default-features = false, features = ["zlib"] }
558 -git2 = "0.15.0"
559 -git2-curl = "0.16.0"
560 +git2 = "0.16.0"
561 +git2-curl = "0.17.0"
562 glob = "0.3.0"
563 hex = "0.4"
564 home = "0.5"
565 @@ -41,7 +42,7 @@ jobserver = "0.1.24"
566 lazycell = "1.2.0"
567 libc = "0.2"
568 log = "0.4.6"
569 -libgit2-sys = "0.14.0"
570 +libgit2-sys = "0.14.1"
571 memchr = "2.1.3"
572 opener = "0.5"
573 os_info = "3.5.0"
574
575 --- cargo-0.66/crates/cargo-test-support/Cargo.toml.orig 2023-01-11 11:33:00.584077593 +0100
576 +++ cargo-0.66/crates/cargo-test-support/Cargo.toml 2023-01-11 11:33:12.564917363 +0100
577 @@ -14,7 +14,7 @@ cargo-util = { path = "../cargo-util" }
578 snapbox = { version = "0.3.0", features = ["diff", "path"] }
579 filetime = "0.2"
580 flate2 = { version = "1.0", default-features = false, features = ["zlib"] }
581 -git2 = "0.15.0"
582 +git2 = "0.16.0"
583 glob = "0.3"
584 itertools = "0.10.0"
585 lazy_static = "1.0"
586