!3 修改gpg验证方式解决编译失败
From: @wk333 Reviewed-by: @caodongxia Signed-off-by: @caodongxia
This commit is contained in:
commit
d16189c90d
111
gpgverify
Executable file
111
gpgverify
Executable file
@ -0,0 +1,111 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
# Copyright 2018 B. Persson, Bjorn@Rombobeorn.se
|
||||||
|
#
|
||||||
|
# This material is provided as is, with absolutely no warranty expressed
|
||||||
|
# or implied. Any use is at your own risk.
|
||||||
|
#
|
||||||
|
# Permission is hereby granted to use or copy this shellscript
|
||||||
|
# for any purpose, provided the above notices are retained on all copies.
|
||||||
|
# Permission to modify the code and to distribute modified code is granted,
|
||||||
|
# provided the above notices are retained, and a notice that the code was
|
||||||
|
# modified is included with the above copyright notice.
|
||||||
|
|
||||||
|
|
||||||
|
function print_help {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: gpgverify --keyring=<pathname> --signature=<pathname> --data=<pathname>
|
||||||
|
|
||||||
|
gpgverify is a wrapper around gpgv designed for easy and safe scripting. It
|
||||||
|
verifies a file against a detached OpenPGP signature and a keyring. The keyring
|
||||||
|
shall contain all the keys that are trusted to certify the authenticity of the
|
||||||
|
file, and must not contain any untrusted keys.
|
||||||
|
|
||||||
|
The differences, compared to invoking gpgv directly, are that gpgverify accepts
|
||||||
|
the keyring in either ASCII-armored or unarmored form, and that it will not
|
||||||
|
accidentally use a default keyring in addition to the specified one.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
--keyring=<pathname> keyring with all the trusted keys and no others
|
||||||
|
--signature=<pathname> detached signature to verify
|
||||||
|
--data=<pathname> file to verify against the signature
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fatal_error() {
|
||||||
|
message="$1" # an error message
|
||||||
|
status=$2 # a number to use as the exit code
|
||||||
|
echo "gpgverify: $message" >&2
|
||||||
|
exit $status
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
require_parameter() {
|
||||||
|
term="$1" # a term for a required parameter
|
||||||
|
value="$2" # Complain and terminate if this value is empty.
|
||||||
|
if test -z "${value}" ; then
|
||||||
|
fatal_error "No ${term} was provided." 2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
check_status() {
|
||||||
|
action="$1" # a string that describes the action that was attempted
|
||||||
|
status=$2 # the exit code of the command
|
||||||
|
if test $status -ne 0 ; then
|
||||||
|
fatal_error "$action failed." $status
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Parse the command line.
|
||||||
|
keyring=
|
||||||
|
signature=
|
||||||
|
data=
|
||||||
|
for parameter in "$@" ; do
|
||||||
|
case "${parameter}" in
|
||||||
|
(--help)
|
||||||
|
print_help
|
||||||
|
exit
|
||||||
|
;;
|
||||||
|
(--keyring=*)
|
||||||
|
keyring="${parameter#*=}"
|
||||||
|
;;
|
||||||
|
(--signature=*)
|
||||||
|
signature="${parameter#*=}"
|
||||||
|
;;
|
||||||
|
(--data=*)
|
||||||
|
data="${parameter#*=}"
|
||||||
|
;;
|
||||||
|
(*)
|
||||||
|
fatal_error "Unknown parameter: \"${parameter}\"" 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
require_parameter 'keyring' "${keyring}"
|
||||||
|
require_parameter 'signature' "${signature}"
|
||||||
|
require_parameter 'data file' "${data}"
|
||||||
|
|
||||||
|
# Make a temporary working directory.
|
||||||
|
workdir="$(mktemp --directory)"
|
||||||
|
check_status 'Making a temporary directory' $?
|
||||||
|
workring="${workdir}/keyring.gpg"
|
||||||
|
|
||||||
|
# Decode any ASCII armor on the keyring. This is harmless if the keyring isn't
|
||||||
|
# ASCII-armored.
|
||||||
|
gpg2 --homedir="${workdir}" --yes --output="${workring}" --dearmor "${keyring}"
|
||||||
|
check_status 'Decoding the keyring' $?
|
||||||
|
|
||||||
|
# Verify the signature using the decoded keyring.
|
||||||
|
gpgv2 --homedir="${workdir}" --keyring="${workring}" "${signature}" "${data}"
|
||||||
|
check_status 'Signature verification' $?
|
||||||
|
|
||||||
|
# (--homedir isn't actually necessary. --dearmor processes only the input file,
|
||||||
|
# and if --keyring is used and contains a slash, then gpgv2 uses only that
|
||||||
|
# keyring. Thus neither command will look for a default keyring, but --homedir
|
||||||
|
# makes extra double sure that no default keyring will be touched in case
|
||||||
|
# another version of GPG works differently.)
|
||||||
|
|
||||||
|
# Clean up. (This is not done in case of an error that may need inspection.)
|
||||||
|
rm --recursive --force ${workdir}
|
||||||
316
macros.gpg
316
macros.gpg
@ -1,316 +0,0 @@
|
|||||||
# The gpg_verify macro is defined further down in this document.
|
|
||||||
|
|
||||||
# gpg_verify takes one option and a list of 2- or 3-tuples.
|
|
||||||
#
|
|
||||||
# With no arguments, attempts to figure everything out. Finds one keyring and
|
|
||||||
# tries to pair each signature file with a source. If there is no source found
|
|
||||||
# which matches a signature, the build is aborted.
|
|
||||||
#
|
|
||||||
# -k gives a common keyring to verify all signatures against, except when an
|
|
||||||
# argument specifies its own keyring.
|
|
||||||
#
|
|
||||||
# Each argument must be of the form "F,S,K" or "F,S", where each of F, S and K
|
|
||||||
# is either the number or the filename of one of the source files in the
|
|
||||||
# package. A pathname including directories is not allowed.
|
|
||||||
# F is a source file to check.
|
|
||||||
# S is a signature.
|
|
||||||
# K is a keyring.
|
|
||||||
#
|
|
||||||
# When an argument specifies a keyring, that signature will be verified against
|
|
||||||
# the keys in that keyring. For arguments that don't specify a keyring, the one
|
|
||||||
# specified with -k will be used, if any. If no keyring is specified either
|
|
||||||
# way, the macro will default to the first one it finds in the source list.
|
|
||||||
#
|
|
||||||
# It is assumed that all the keys in all keyrings, whether automatically found
|
|
||||||
# or explicitly specified, are trusted to authenticate the source files. There
|
|
||||||
# must not be any untrusted keys included.
|
|
||||||
|
|
||||||
# Some utility functions to the global namespace
|
|
||||||
# Most of these should come from the utility macros in the other repo.
|
|
||||||
%define gpg_macros_init %{lua:
|
|
||||||
function db(str)
|
|
||||||
io.stderr:write(tostring(str) .. '\\n')
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Simple basename clone
|
|
||||||
function basename(str)
|
|
||||||
local name = string.gsub(str, "(.*/)(.*)", "%2")
|
|
||||||
return name
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Get the numbered or source file.
|
|
||||||
-- The spec writer can use any numbering scheme. The sources table
|
|
||||||
-- always counts from 1 and has no gaps, so we have to go back to the
|
|
||||||
-- SOURCEN macros.
|
|
||||||
function get_numbered_source(num)
|
|
||||||
local macro = "%SOURCE" .. num
|
|
||||||
local val = rpm.expand(macro)
|
|
||||||
if val == macro then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
-- Get the named source file. This returns the full path to a source file,
|
|
||||||
-- or nil if no such source exists.
|
|
||||||
function get_named_source(name)
|
|
||||||
local path
|
|
||||||
for _,path in ipairs(sources) do
|
|
||||||
if name == basename(path) then
|
|
||||||
return path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Determine whether the supplied filename contains a signature
|
|
||||||
-- Assumes the file will be closed when the handle goes out of scope
|
|
||||||
function is_signature(fname)
|
|
||||||
-- I don't really like this, but you can have completely binary sigs
|
|
||||||
if string.find(fname, '%.sig$') then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
local file = io.open(fname, 'r')
|
|
||||||
if file == nil then return false end
|
|
||||||
\
|
|
||||||
local c = 1
|
|
||||||
while true do
|
|
||||||
local line = file:read('*line')
|
|
||||||
if (line == nil or c > 10) then break end
|
|
||||||
if string.find(line, "BEGIN PGP SIGNATURE") then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
c = c+1
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Determine whether the supplied filename looks like a keyring
|
|
||||||
-- Ends in .gpg (might be binary data)? Contains "BEGIN PGP PUBLIC KEY BLOCK"
|
|
||||||
function is_keyring(fname)
|
|
||||||
-- XXX Have to hack this now to make it not find macros.gpg while we're testing.
|
|
||||||
if string.find(fname, '%.gpg$') and not string.find(fname, 'macros.gpg$') then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
\
|
|
||||||
local file = io.open(fname, 'r')
|
|
||||||
if file == nil then return false end
|
|
||||||
io.input(file)
|
|
||||||
local c = 1
|
|
||||||
while true do
|
|
||||||
local line = io.read('*line')
|
|
||||||
if (line == nil or c > 10) then break end
|
|
||||||
if string.find(line, "BEGIN PGP PUBLIC KEY BLOCK") then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
c = c+1
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Output code to have the current scriptlet echo something
|
|
||||||
function echo(str)
|
|
||||||
print("echo " .. str .. "\\n")
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Output an exit statement with nonzero return to the current scriptlet
|
|
||||||
function exit()
|
|
||||||
print("exit 1\\n")
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Call the RPM %error macro
|
|
||||||
function rpmerror(str)
|
|
||||||
echo("gpg_verify: " .. str)
|
|
||||||
rpm.expand("%{error:gpg_verify: " .. str .. "}")
|
|
||||||
exit(1)
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- XXX How to we get just a flag and no option?
|
|
||||||
function getflag(flag)
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Extract the value of a passed option
|
|
||||||
function getoption(opt)
|
|
||||||
out = rpm.expand("%{-" .. opt .. "*}")
|
|
||||||
-- if string.len(out) == 0 then
|
|
||||||
if #out == 0 then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
return out
|
|
||||||
end
|
|
||||||
\
|
|
||||||
function unknownarg(a)
|
|
||||||
rpmerror("Unknown argument to %%gpg_verify: " .. a)
|
|
||||||
end
|
|
||||||
\
|
|
||||||
function rprint(s, l, i) -- recursive Print (structure, limit, indent)
|
|
||||||
l = (l) or 100; i = i or ""; -- default item limit, indent string
|
|
||||||
if (l<1) then db("ERROR: Item limit reached."); return l-1 end;
|
|
||||||
local ts = type(s);
|
|
||||||
if (ts ~= "table") then db(i,ts,s); return l-1 end
|
|
||||||
db(i,ts); -- print "table"
|
|
||||||
for k,v in pairs(s) do -- db("[KEY] VALUE")
|
|
||||||
l = rprint(v, l, i.."\t["..tostring(k).."]");
|
|
||||||
if (l < 0) then break end
|
|
||||||
end
|
|
||||||
return l
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Given a list of source file numbers or file names, validate them and
|
|
||||||
-- convert them to a list of full filenames.
|
|
||||||
function check_sources_list(arr)
|
|
||||||
local files = {}
|
|
||||||
local src,fpath
|
|
||||||
for _, src in ipairs(arr) do
|
|
||||||
if tonumber(src) then
|
|
||||||
-- We have a number; turn it to a full path to the corresponding source file
|
|
||||||
fpath = get_numbered_source(src)
|
|
||||||
else
|
|
||||||
fpath = get_named_source(src)
|
|
||||||
end
|
|
||||||
if not src then
|
|
||||||
err = 'Not a valid source: ' .. src
|
|
||||||
if src == '1' then
|
|
||||||
err = err .. '. Note that "Source:" is the 0th source file, not the 1st.'
|
|
||||||
end
|
|
||||||
rpmerror(err)
|
|
||||||
end
|
|
||||||
table.insert(files, fpath)
|
|
||||||
end
|
|
||||||
return files
|
|
||||||
end
|
|
||||||
rpm.define("gpg_macros_init %{nil}")
|
|
||||||
}#
|
|
||||||
|
|
||||||
# The actual macro
|
|
||||||
%define gpg_verify(k:) %gpg_macros_init%{lua:
|
|
||||||
-- RPM will ignore the first thing we output unless we give it a newline.
|
|
||||||
print('\\n')
|
|
||||||
\
|
|
||||||
local defkeyspec = getoption("k")
|
|
||||||
local args = rpm.expand("%*")
|
|
||||||
local sourcefiles = {}
|
|
||||||
local signature_table = {}
|
|
||||||
local signatures = {}
|
|
||||||
local keyrings = {}
|
|
||||||
local defkey, match, captures, s
|
|
||||||
\
|
|
||||||
local function storematch(m, c)
|
|
||||||
match = m; captures = c
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Scan all of the sources and try to categorize them.
|
|
||||||
-- Move to a function
|
|
||||||
for i,s in pairs(sources) do
|
|
||||||
sourcefiles[s] = true
|
|
||||||
-- db('File: ' .. i .. ", " .. s)
|
|
||||||
if is_signature(s) then
|
|
||||||
table.insert(signatures, s)
|
|
||||||
signature_table[s] = true
|
|
||||||
db('Found signature: ' .. s)
|
|
||||||
elseif is_keyring(s) then
|
|
||||||
table.insert(keyrings, s)
|
|
||||||
db('Found keyring: ' .. s)
|
|
||||||
else
|
|
||||||
-- Must be a source
|
|
||||||
db('Found source: ' .. s)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
\
|
|
||||||
if defkeyspec then
|
|
||||||
defkey = check_sources_list({defkeyspec})[1]
|
|
||||||
if not defkey then
|
|
||||||
rpmerror('The provided keyring ' .. defkeyspec .. ' is not a valid source number or filename.')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
\
|
|
||||||
if defkey then
|
|
||||||
db('Defkey: ' .. defkey)
|
|
||||||
else
|
|
||||||
db('No common key yet')
|
|
||||||
if keyrings[1] then
|
|
||||||
defkey = keyrings[1]
|
|
||||||
db('Using first found keyring file: '..defkey)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Check over any given args to make sure they're valid, and to see if a
|
|
||||||
-- common key is required.
|
|
||||||
local needdefkey = false
|
|
||||||
local double = rex.newPOSIX('^([^,]+),([^,]+)$')
|
|
||||||
local triple = rex.newPOSIX('^([^,]+),([^,]+),([^,]+)$')
|
|
||||||
local arglist = {}
|
|
||||||
\
|
|
||||||
-- RPM gives us the arguments in a single string.
|
|
||||||
-- Split on spaces and iterate
|
|
||||||
for arg in args:gmatch('%S+') do
|
|
||||||
db('Checking ' .. arg)
|
|
||||||
if triple:gmatch(arg, storematch) > 0 then
|
|
||||||
db('Looks OK')
|
|
||||||
local parsed = {srcnum=captures[1], signum=captures[2], keynum=captures[3]}
|
|
||||||
s = check_sources_list({captures[1], captures[2], captures[3]})
|
|
||||||
parsed.srcfile = s[1]
|
|
||||||
parsed.sigfile = s[2]
|
|
||||||
parsed.keyfile = s[3]
|
|
||||||
table.insert(arglist, parsed)
|
|
||||||
elseif double:gmatch(arg, storematch) > 0 then
|
|
||||||
db('Looks OK; needs common key')
|
|
||||||
needdefkey = true
|
|
||||||
local parsed = {srcnum=captures[1], signum=captures[2], keynum=defkeyspec, keyfile=defkey}
|
|
||||||
s = check_sources_list({captures[1], captures[2]})
|
|
||||||
parsed.srcfile = s[1]
|
|
||||||
parsed.sigfile = s[2]
|
|
||||||
table.insert(arglist, parsed)
|
|
||||||
else
|
|
||||||
rpmerror('Provided argument '..arg..' is not valid.')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- So we now know if one of those args needs a common key
|
|
||||||
if needdefkey and not defkey then
|
|
||||||
rpmerror('No common key was specified or found, yet the arguments require one.')
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- And if we have no arguments at all and no common key was found,
|
|
||||||
-- then we can't do an automatic check
|
|
||||||
if not defkey and args == '' then
|
|
||||||
rpmerror('No keyring specified and none found; cannot auto-check.')
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Nothing to check means automatic mode
|
|
||||||
if #arglist == 0 then
|
|
||||||
local noext
|
|
||||||
for i,_ in pairs(signature_table) do
|
|
||||||
-- Find the name without the extension
|
|
||||||
noext = string.gsub(i, '%.[^.]+$', '')
|
|
||||||
if sourcefiles[noext] then
|
|
||||||
table.insert(arglist, {srcfile=noext, sigfile=i, keyfile=defkey})
|
|
||||||
else
|
|
||||||
rpmerror('Found signature ' .. i .. ' with no matching source file.')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
\
|
|
||||||
-- Now actually check things
|
|
||||||
for _,arg in ipairs(arglist) do
|
|
||||||
local gpgfile = '$GPGHOME/' .. basename(arg.keyfile) .. '.gpg'
|
|
||||||
echo('Checking signature: file ' .. arg.srcfile .. ' sig ' .. arg.sigfile .. ' key ' .. arg.keyfile)
|
|
||||||
\
|
|
||||||
-- We need a secure temp directorry
|
|
||||||
print('GPGHOME=$(mktemp -qd)\\n')
|
|
||||||
\
|
|
||||||
-- Call gpg2 to generate the dearmored key
|
|
||||||
print('gpg2 --homedir $GPGHOME --no-default-keyring --quiet --yes ')
|
|
||||||
print('--output '.. gpgfile .. ' --dearmor ' .. arg.keyfile .. "\\n")
|
|
||||||
\
|
|
||||||
-- Call gpgv2 to verify the signature against the source file with the dearmored key
|
|
||||||
print('gpgv2 --homedir $GPGHOME --keyring ' .. gpgfile .. ' ' .. arg.sigfile .. ' ' .. arg.srcfile .. '\\n')
|
|
||||||
\
|
|
||||||
print('rm -rf $GPGHOME\\n')
|
|
||||||
echo('')
|
|
||||||
end
|
|
||||||
\
|
|
||||||
db('------------')
|
|
||||||
}#
|
|
||||||
|
|
||||||
# vim: set filetype=spec:
|
|
||||||
@ -15,7 +15,7 @@
|
|||||||
%{!?_pkgdocdir: %global _pkgdocdir %{_docdir}/%{name}-%{version}}
|
%{!?_pkgdocdir: %global _pkgdocdir %{_docdir}/%{name}-%{version}}
|
||||||
Name: openconnect
|
Name: openconnect
|
||||||
Version: 8.10
|
Version: 8.10
|
||||||
Release: 1
|
Release: 2
|
||||||
Summary: Open client for Cisco AnyConnect VPN, Juniper Network Connect/Pulse, PAN GlobalProtect
|
Summary: Open client for Cisco AnyConnect VPN, Juniper Network Connect/Pulse, PAN GlobalProtect
|
||||||
License: LGPLv2+
|
License: LGPLv2+
|
||||||
URL: http://www.infradead.org/openconnect.html
|
URL: http://www.infradead.org/openconnect.html
|
||||||
@ -24,7 +24,7 @@ Source0: ftp://ftp.infradead.org/pub/openconnect/openconnect-%{versi
|
|||||||
Source1: ftp://ftp.infradead.org/pub/openconnect/openconnect-%{version}%{?gitsuffix}.tar.gz.asc
|
Source1: ftp://ftp.infradead.org/pub/openconnect/openconnect-%{version}%{?gitsuffix}.tar.gz.asc
|
||||||
%endif
|
%endif
|
||||||
Source2: gpgkey-BE07D9FD54809AB2C4B0FF5F63762CDA67E2F359.asc
|
Source2: gpgkey-BE07D9FD54809AB2C4B0FF5F63762CDA67E2F359.asc
|
||||||
Source3: macros.gpg
|
Source3: gpgverify
|
||||||
BuildRequires: pkgconfig(libxml-2.0) pkgconfig(libpcsclite) krb5-devel gnupg2
|
BuildRequires: pkgconfig(libxml-2.0) pkgconfig(libpcsclite) krb5-devel gnupg2
|
||||||
BuildRequires: autoconf automake libtool gettext pkgconfig(liblz4)
|
BuildRequires: autoconf automake libtool gettext pkgconfig(liblz4)
|
||||||
BuildRequires: pkgconfig(uid_wrapper) pkgconfig(socket_wrapper)
|
BuildRequires: pkgconfig(uid_wrapper) pkgconfig(socket_wrapper)
|
||||||
@ -60,11 +60,11 @@ Requires: %{name}%{?_isa} = %{version}-%{release}
|
|||||||
This package provides the core HTTP and authentication support from
|
This package provides the core HTTP and authentication support from
|
||||||
the OpenConnect VPN client, to be used by GUI authentication dialogs
|
the OpenConnect VPN client, to be used by GUI authentication dialogs
|
||||||
for NetworkManager etc.
|
for NetworkManager etc.
|
||||||
%include %SOURCE3
|
|
||||||
|
|
||||||
%prep
|
%prep
|
||||||
%if 0%{?gitcount} == 0
|
%if 0%{?gitcount} == 0
|
||||||
%gpg_verify
|
cp %{SOURCE3} .
|
||||||
|
sh gpgverify --keyring='%{SOURCE2}' --signature='%{SOURCE1}' --data='%{SOURCE0}'
|
||||||
%endif
|
%endif
|
||||||
%autosetup -n openconnect-%{version}%{?gitsuffix} -p1
|
%autosetup -n openconnect-%{version}%{?gitsuffix} -p1
|
||||||
|
|
||||||
@ -103,5 +103,8 @@ make VERBOSE=1 check
|
|||||||
%{_libdir}/pkgconfig/openconnect.pc
|
%{_libdir}/pkgconfig/openconnect.pc
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Tue Feb 07 2023 wangkai <wangkai385@h-partners.com> - 8.10-2
|
||||||
|
- modify the gpg verification method
|
||||||
|
|
||||||
* Mon Sep 6 2021 wulei <wulei80@huawei.com> - 8.10-1
|
* Mon Sep 6 2021 wulei <wulei80@huawei.com> - 8.10-1
|
||||||
- package init
|
- package init
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user