Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions usr/lib/linuxmint/mintSources/apt-key.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/bin/bash
# apt-key-clone: A drop-in replacement for apt-key on Debian 13
# Maps legacy commands to all modern and legacy keyring directories.

# Strip up to two dashes to normalize (e.g., --list becomes list)
COMMAND="${1#-}"
COMMAND="${COMMAND#-}"

# Define all possible key locations to scan
SCAN_PATHS=(
"/etc/apt/trusted.gpg"
"/etc/apt/trusted.gpg.d"/*
"/etc/apt/keyrings"/*
# "/usr/share/keyrings"/*
)

case "$COMMAND" in
list)
for keyfile in "${SCAN_PATHS[@]}"; do
# The [ -f ] check safely skips missing directories like /etc/apt/trusted.gpg
[ -f "$keyfile" ] || continue
if file "$keyfile" | grep -qi -E 'PGP|GPG'; then
echo "--------------------------------------------------------"
echo "Keyring: $keyfile"
echo "--------------------------------------------------------"
gpg --show-keys --with-fingerprint "$keyfile" 2>/dev/null
echo ""
fi
done
;;

add)
if [ "$EUID" -ne 0 ]; then echo "ERROR: Root privileges required."; exit 1; fi
FILE="$2"
if [ -z "$FILE" ]; then echo "Usage: apt-key add <file>"; exit 1; fi

# Capture file or STDIN
TMP_INPUT=$(mktemp)
if [ "$FILE" = "-" ]; then
cat > "$TMP_INPUT"
else
cat "$FILE" > "$TMP_INPUT"
fi

# Extract the fingerprint to name the file safely
FPR=$(gpg --show-keys --with-colons "$TMP_INPUT" 2>/dev/null | grep "^fpr:" | head -n 1 | cut -d: -f10)
if [ -z "$FPR" ]; then
echo "ERROR: Could not extract a valid GPG key."
rm -f "$TMP_INPUT"
exit 1
fi

# We save to trusted.gpg.d so the key is globally trusted, mimicking legacy apt-key behavior
DEST="/etc/apt/trusted.gpg.d/imported-${FPR}.gpg"

# Import to a temporary keyring, then export cleanly as a dearmored binary
# This handles both ASCII (.asc) and binary (.gpg) files perfectly.
TMP_RING=$(mktemp)
gpg --no-default-keyring --keyring "$TMP_RING" --import "$TMP_INPUT" >/dev/null 2>&1
gpg --no-default-keyring --keyring "$TMP_RING" --export > "$DEST"

chmod 644 "$DEST"
rm -f "$TMP_INPUT" "$TMP_RING" "${TMP_RING}~"
echo "OK"
;;

del)
if [ "$EUID" -ne 0 ]; then echo "ERROR: Root privileges required."; exit 1; fi
KEYID="$2"
if [ -z "$KEYID" ]; then echo "Usage: apt-key del <keyid>"; exit 1; fi

# Remove spaces in case the user passed a full fingerprint
KEYID=$(echo "$KEYID" | tr -d ' ')
FOUND=0

for keyfile in "${SCAN_PATHS[@]}"; do
[ -f "$keyfile" ] || continue

# Check if the keyid exists in this specific keyring file
if gpg --show-keys --with-colons "$keyfile" 2>/dev/null | grep -qF "${KEYID}"; then
KEY_COUNT=$(gpg --show-keys --with-colons "$keyfile" 2>/dev/null | grep -c "^pub:")

if [ "$KEY_COUNT" -le 1 ]; then
# If it's the only key in the file, delete the file entirely
rm -f "$keyfile"
else
# If it's a multi-key file, delete just the specific key
gpg --no-default-keyring --keyring "$keyfile" --batch --yes --delete-keys "$KEYID" >/dev/null 2>&1
rm -f "${keyfile}~" # Clean up GPG backup file
fi
FOUND=1
fi
done

if [ $FOUND -eq 1 ]; then
echo "OK"
else
echo "Warning: Key not found."
fi
;;

*)
echo "Usage: apt-key {add <file> | del <keyid> | list}"
exit 1
;;
esac
10 changes: 6 additions & 4 deletions usr/lib/linuxmint/mintSources/mintSources.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

from io import BytesIO
from CountryInformation import CountryInformation
from pathlib import Path

import apt_pkg

Expand All @@ -41,6 +42,7 @@
disable_refresh = False
sources_changed = False

script_dir = Path(__file__).parent
FLAG_PATH = "/usr/share/iso-flag-png/%s.png"
FLAG_SIZE = 16

Expand Down Expand Up @@ -340,7 +342,7 @@ def __init__(self, pub):
self.uid = ""

def delete(self):
subprocess.call(["apt-key", "del", self.pub])
subprocess.run(["./apt-key.sh", "del", self.pub], cwd=script_dir, capture_output=True, text=True)

def get_name(self):
return "%s\n<small> %s</small>" % (GLib.markup_escape_text(self.uid), GLib.markup_escape_text(self.pub))
Expand Down Expand Up @@ -1293,9 +1295,9 @@ def __init__(self, path, uri):
def load_keys(self):
self.keys = []
key = None
output = subprocess.getoutput("apt-key list")
output = subprocess.run(["./apt-key.sh", "list"], cwd=script_dir, capture_output=True, text=True)
lines = []
for line in output.split("\n"):
for line in output.stdout.split("\n"):
line = line.strip()
if line.startswith("/etc/apt"):
continue
Expand Down Expand Up @@ -1332,7 +1334,7 @@ def add_key(self, widget):
dialog.set_default_response(Gtk.ResponseType.OK)
response = dialog.run()
if response == Gtk.ResponseType.OK:
subprocess.call(["apt-key", "add", dialog.get_filename()])
subprocess.run(["./apt-key.sh", "add", dialog.get_filename()], cwd=script_dir, capture_output=True, text=True)
self.load_keys()
self.enable_reload_button()
dialog.destroy()
Expand Down