From mboxrd@z Thu Jan 1 00:00:00 1970 From: Michael Tremer To: development@lists.ipfire.org Subject: Re: [PATCH 1/4] ca-certificates: Update to work with python3 version of certdata2pem.py Date: Mon, 23 Aug 2021 11:34:03 +0100 Message-ID: In-Reply-To: <20210820200428.3535766-1-adolf.belka@ipfire.org> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============6921806315604488173==" List-Id: --===============6921806315604488173== Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Reviewed-by: Michael Tremer > On 20 Aug 2021, at 21:04, Adolf Belka wrote: >=20 > - Implement python3 version of certdata2pem.py script from fedora > - Modify build.sh to work with python3 script that uses p11-kit based on fe= dora > approach - https://src.fedoraproject.org/rpms/ca-certificates/tree/rawhide > - Extraction of cert files now uses p11-kit which requires libtasn1 as a bu= ild > dependency > - Updated rootfile > - Updated ca-certificates installed into a vm and confirmed to download a f= ile from an > https site with the same results as with existing ca-certfictaes system >=20 > Tested-by: Adolf Belka > Signed-off-by: Adolf Belka > --- > config/ca-certificates/build.sh | 48 +++-- > config/ca-certificates/certdata2pem.py | 260 ++++++++++++++++++++---- > config/rootfiles/common/ca-certificates | 5 +- > lfs/ca-certificates | 2 +- > 4 files changed, 248 insertions(+), 67 deletions(-) >=20 > diff --git a/config/ca-certificates/build.sh b/config/ca-certificates/build= .sh > index c868ed94a..8e64f9e9f 100644 > --- a/config/ca-certificates/build.sh > +++ b/config/ca-certificates/build.sh > @@ -3,13 +3,34 @@ > set -e >=20 > # Create file layout. > -mkdir -pv certs certs/legacy-default certs/legacy-disable > +mkdir -pv certs > +mkdir -pv /etc/pki/ca-trust/source > cp certdata.txt certs > cd certs >=20 > -python ../certdata2pem.py > +python3 ../certdata2pem.py >=20 > cd .. > + > + > +cat < ca-bundle.trust.p11-kit > +# This is a bundle of X.509 certificates of public Certificate > +# Authorities. It was generated from the Mozilla root CA list. > +# These certificates and trust/distrust attributes use the file format acc= epted > +# by the p11-kit-trust module. > +# > +# Source: mozilla/security/nss/lib/ckfw/builtins/certdata.txt > +# > +EOF > + > + > +P11FILES=3D`find certs -name \*.tmp-p11-kit | wc -l` > +if [ $P11FILES -ne 0 ]; then > + for p in certs/*.tmp-p11-kit; do=20 > + cat "$p" >> /etc/pki/ca-trust/source/ca-bundle.trust.p11-kit > + done=09 > +fi > + > cat < ca-bundle.crt > # This is a bundle of X.509 certificates of public Certificate > # Authorities. It was generated from the Mozilla root CA list. > @@ -28,24 +49,11 @@ cat < ca-bundle.trust.crt > # > EOF >=20 > -for f in certs/*.crt; do=20 > - [ -z "${f}" ] && continue > - > - tbits=3D$(sed -n '/^# openssl-trust/{s/^.*=3D//;p;}' ${f}) > - case "${tbits}" in > - *serverAuth*) > - openssl x509 -text -in "${f}" >> ca-bundle.crt > - ;; > - esac > +trust extract --comment --filter=3Dcertificates --format=3Dopenssl-bundle = --overwrite ca-bundle.trust > +cat ca-bundle.trust >> ca-bundle.trust.crt >=20 > - if [ -n "$tbits" ]; then > - targs=3D"" > - for t in ${tbits}; do > - targs=3D"${targs} -addtrust ${t}" > - done > +trust extract --comment --filter=3Dca-anchors --format=3Dpem-bundle --over= write --purpose server-auth ca-bundle > +cat ca-bundle >> ca-bundle.crt >=20 > - openssl x509 -text -in "${f}" -trustout $targs >> ca-bundle.trust.crt > - fi > -done >=20 > -exit 0 > +exit 0 > \ No newline at end of file > diff --git a/config/ca-certificates/certdata2pem.py b/config/ca-certificate= s/certdata2pem.py > index 44cc9e03b..a52ce9c74 100644 > --- a/config/ca-certificates/certdata2pem.py > +++ b/config/ca-certificates/certdata2pem.py > @@ -26,16 +26,17 @@ import os.path > import re > import sys > import textwrap > -import urllib > +import urllib.request, urllib.parse, urllib.error > +import subprocess >=20 > objects =3D [] >=20 > def printable_serial(obj): > - return ".".join(map(lambda x:str(ord(x)), obj['CKA_SERIAL_NUMBER'])) > + return ".".join([str(x) for x in obj['CKA_SERIAL_NUMBER']]) >=20 > # Dirty file parser. > in_data, in_multiline, in_obj =3D False, False, False > -field, type, value, obj =3D None, None, None, dict() > +field, ftype, value, binval, obj =3D None, None, None, bytearray(), dict() > for line in open('certdata.txt', 'r'): > # Ignore the file header. > if not in_data: > @@ -55,33 +56,36 @@ for line in open('certdata.txt', 'r'): > continue > if in_multiline: > if not line.startswith('END'): > - if type =3D=3D 'MULTILINE_OCTAL': > + if ftype =3D=3D 'MULTILINE_OCTAL': > line =3D line.strip() > for i in re.finditer(r'\\([0-3][0-7][0-7])', line): > - value +=3D chr(int(i.group(1), 8)) > + integ =3D int(i.group(1), 8) > + binval.extend((integ).to_bytes(1, sys.byteorder)) > + obj[field] =3D binval > else: > value +=3D line > + obj[field] =3D value > continue > - obj[field] =3D value > in_multiline =3D False > continue > if line.startswith('CKA_CLASS'): > in_obj =3D True > line_parts =3D line.strip().split(' ', 2) > if len(line_parts) > 2: > - field, type =3D line_parts[0:2] > + field, ftype =3D line_parts[0:2] > value =3D ' '.join(line_parts[2:]) > elif len(line_parts) =3D=3D 2: > - field, type =3D line_parts > + field, ftype =3D line_parts > value =3D None > else: > - raise NotImplementedError, 'line_parts < 2 not supported.\n' + line > - if type =3D=3D 'MULTILINE_OCTAL': > + raise NotImplementedError('line_parts < 2 not supported.\n' + line) > + if ftype =3D=3D 'MULTILINE_OCTAL': > in_multiline =3D True > value =3D "" > + binval =3D bytearray() > continue > obj[field] =3D value > -if len(obj.items()) > 0: > +if len(list(obj.items())) > 0: > objects.append(obj) >=20 > # Build up trust database. > @@ -91,7 +95,7 @@ for obj in objects: > continue > key =3D obj['CKA_LABEL'] + printable_serial(obj) > trustmap[key] =3D obj > - print " added trust", key > + print(" added trust", key) >=20 > # Build up cert database. > certmap =3D dict() > @@ -100,7 +104,7 @@ for obj in objects: > continue > key =3D obj['CKA_LABEL'] + printable_serial(obj) > certmap[key] =3D obj > - print " added cert", key > + print(" added cert", key) >=20 > def obj_to_filename(obj): > label =3D obj['CKA_LABEL'][1:-1] > @@ -109,10 +113,32 @@ def obj_to_filename(obj): > .replace('(', '=3D')\ > .replace(')', '=3D')\ > .replace(',', '_') > - label =3D re.sub(r'\\x[0-9a-fA-F]{2}', lambda m:chr(int(m.group(0)[2:]= , 16)), label) > + labelbytes =3D bytearray() > + i =3D 0 > + imax =3D len(label) > + while i < imax: > + if i < imax-3 and label[i] =3D=3D '\\' and label[i+1] =3D=3D 'x': > + labelbytes.extend(bytes.fromhex(label[i+2:i+4])) > + i +=3D 4 > + continue > + labelbytes.extend(str.encode(label[i])) > + i =3D i+1 > + continue > + label =3D labelbytes.decode('utf-8') > serial =3D printable_serial(obj) > return label + ":" + serial >=20 > +def write_cert_ext_to_file(f, oid, value, public_key): > + f.write("[p11-kit-object-v1]\n") > + f.write("label: "); > + f.write(tobj['CKA_LABEL']) > + f.write("\n") > + f.write("class: x-certificate-extension\n"); > + f.write("object-id: " + oid + "\n") > + f.write("value: \"" + value + "\"\n") > + f.write("modifiable: false\n"); > + f.write(public_key) > + > trust_types =3D { > "CKA_TRUST_DIGITAL_SIGNATURE": "digital-signature", > "CKA_TRUST_NON_REPUDIATION": "non-repudiation", > @@ -151,34 +177,39 @@ openssl_trust =3D { > "CKA_TRUST_EMAIL_PROTECTION": "emailProtection", > } >=20 > +cert_distrust_types =3D { > + "CKA_NSS_SERVER_DISTRUST_AFTER": "nss-server-distrust-after", > + "CKA_NSS_EMAIL_DISTRUST_AFTER": "nss-email-distrust-after", > +} > + > for tobj in objects: > if tobj['CKA_CLASS'] =3D=3D 'CKO_NSS_TRUST': > key =3D tobj['CKA_LABEL'] + printable_serial(tobj) > - print "producing trust for " + key > + print("producing trust for " + key) > trustbits =3D [] > distrustbits =3D [] > openssl_trustflags =3D [] > openssl_distrustflags =3D [] > legacy_trustbits =3D [] > legacy_openssl_trustflags =3D [] > - for t in trust_types.keys(): > - if tobj.has_key(t) and tobj[t] =3D=3D 'CKT_NSS_TRUSTED_DELEGAT= OR': > + for t in list(trust_types.keys()): > + if t in tobj and tobj[t] =3D=3D 'CKT_NSS_TRUSTED_DELEGATOR': > trustbits.append(t) > if t in openssl_trust: > openssl_trustflags.append(openssl_trust[t]) > - if tobj.has_key(t) and tobj[t] =3D=3D 'CKT_NSS_NOT_TRUSTED': > + if t in tobj and tobj[t] =3D=3D 'CKT_NSS_NOT_TRUSTED': > distrustbits.append(t) > if t in openssl_trust: > openssl_distrustflags.append(openssl_trust[t]) >=20 > - for t in legacy_trust_types.keys(): > - if tobj.has_key(t) and tobj[t] =3D=3D 'CKT_NSS_TRUSTED_DELEGAT= OR': > + for t in list(legacy_trust_types.keys()): > + if t in tobj and tobj[t] =3D=3D 'CKT_NSS_TRUSTED_DELEGATOR': > real_t =3D legacy_to_real_trust_types[t] > legacy_trustbits.append(real_t) > if real_t in openssl_trust: > legacy_openssl_trustflags.append(openssl_trust[real_t]) > - if tobj.has_key(t) and tobj[t] =3D=3D 'CKT_NSS_NOT_TRUSTED': > - raise NotImplementedError, 'legacy distrust not supported.= \n' + line > + if t in tobj and tobj[t] =3D=3D 'CKT_NSS_NOT_TRUSTED': > + raise NotImplementedError('legacy distrust not supported.\= n' + line) >=20 > fname =3D obj_to_filename(tobj) > try: > @@ -186,43 +217,181 @@ for tobj in objects: > except: > obj =3D None >=20 > - if obj !=3D None: > - fname +=3D ".crt" > - else: > - fname +=3D ".p11-kit" > + # optional debug code, that dumps the parsed input to files > + #fulldump =3D "dump-" + fname > + #dumpf =3D open(fulldump, 'w') > + #dumpf.write(str(obj)); > + #dumpf.write(str(tobj)); > + #dumpf.close(); >=20 > is_legacy =3D 0 > - if tobj.has_key('LEGACY_CKA_TRUST_SERVER_AUTH') or tobj.has_key('L= EGACY_CKA_TRUST_EMAIL_PROTECTION') or tobj.has_key('LEGACY_CKA_TRUST_CODE_SIG= NING'): > + if 'LEGACY_CKA_TRUST_SERVER_AUTH' in tobj or 'LEGACY_CKA_TRUST_EMA= IL_PROTECTION' in tobj or 'LEGACY_CKA_TRUST_CODE_SIGNING' in tobj: > is_legacy =3D 1 > if obj =3D=3D None: > - raise NotImplementedError, 'found legacy trust without cer= tificate.\n' + line > - legacy_fname =3D "legacy-default/" + fname > + raise NotImplementedError('found legacy trust without cert= ificate.\n' + line) > + > + legacy_fname =3D "legacy-default/" + fname + ".crt" > f =3D open(legacy_fname, 'w') > f.write("# alias=3D%s\n"%tobj['CKA_LABEL']) > f.write("# trust=3D" + " ".join(legacy_trustbits) + "\n") > if legacy_openssl_trustflags: > f.write("# openssl-trust=3D" + " ".join(legacy_openssl_trus= tflags) + "\n") > f.write("-----BEGIN CERTIFICATE-----\n") > - f.write("\n".join(textwrap.wrap(base64.b64encode(obj['CKA_VALU= E']), 64))) > + temp_encoded_b64 =3D base64.b64encode(obj['CKA_VALUE']) > + temp_wrapped =3D textwrap.wrap(temp_encoded_b64.decode(), 64) > + f.write("\n".join(temp_wrapped)) > f.write("\n-----END CERTIFICATE-----\n") > f.close() > - if tobj.has_key('CKA_TRUST_SERVER_AUTH') or tobj.has_key('CKA_= TRUST_EMAIL_PROTECTION') or tobj.has_key('CKA_TRUST_CODE_SIGNING'): > - fname =3D "legacy-disable/" + fname > - else: > - continue >=20 > + if 'CKA_TRUST_SERVER_AUTH' in tobj or 'CKA_TRUST_EMAIL_PROTECT= ION' in tobj or 'CKA_TRUST_CODE_SIGNING' in tobj: > + legacy_fname =3D "legacy-disable/" + fname + ".crt" > + f =3D open(legacy_fname, 'w') > + f.write("# alias=3D%s\n"%tobj['CKA_LABEL']) > + f.write("# trust=3D" + " ".join(trustbits) + "\n") > + if openssl_trustflags: > + f.write("# openssl-trust=3D" + " ".join(openssl_trustf= lags) + "\n") > + f.write("-----BEGIN CERTIFICATE-----\n") > + f.write("\n".join(textwrap.wrap(base64.b64encode(obj['CKA_= VALUE']), 64))) > + f.write("\n-----END CERTIFICATE-----\n") > + f.close() > + > + # don't produce p11-kit output for legacy certificates > + continue > + > + pk =3D '' > + cert_comment =3D '' > + if obj !=3D None: > + # must extract the public key from the cert, let's use openssl > + cert_fname =3D "cert-" + fname > + fc =3D open(cert_fname, 'w') > + fc.write("-----BEGIN CERTIFICATE-----\n") > + temp_encoded_b64 =3D base64.b64encode(obj['CKA_VALUE']) > + temp_wrapped =3D textwrap.wrap(temp_encoded_b64.decode(), 64) > + fc.write("\n".join(temp_wrapped)) > + fc.write("\n-----END CERTIFICATE-----\n") > + fc.close(); > + pk_fname =3D "pubkey-" + fname > + fpkout =3D open(pk_fname, "w") > + dump_pk_command =3D ["openssl", "x509", "-in", cert_fname, "-n= oout", "-pubkey"] > + subprocess.call(dump_pk_command, stdout=3Dfpkout) > + fpkout.close() > + with open (pk_fname, "r") as myfile: > + pk=3Dmyfile.read() > + # obtain certificate information suitable as a comment > + comment_fname =3D "comment-" + fname > + fcout =3D open(comment_fname, "w") > + comment_command =3D ["openssl", "x509", "-in", cert_fname, "-n= oout", "-text"] > + subprocess.call(comment_command, stdout=3Dfcout) > + fcout.close() > + sed_command =3D ["sed", "--in-place", "s/^/#/", comment_fname] > + subprocess.call(sed_command) > + with open (comment_fname, "r", errors =3D 'replace') as myfile: > + cert_comment=3Dmyfile.read() > + > + fname +=3D ".tmp-p11-kit" > f =3D open(fname, 'w') > + > if obj !=3D None: > - f.write("# alias=3D%s\n"%tobj['CKA_LABEL']) > - f.write("# trust=3D" + " ".join(trustbits) + "\n") > - f.write("# distrust=3D" + " ".join(distrustbits) + "\n") > - if openssl_trustflags: > - f.write("# openssl-trust=3D" + " ".join(openssl_trustflags= ) + "\n") > - if openssl_distrustflags: > - f.write("# openssl-distrust=3D" + " ".join(openssl_distrus= tflags) + "\n") > + is_distrusted =3D False > + has_server_trust =3D False > + has_email_trust =3D False > + has_code_trust =3D False > + > + if 'CKA_TRUST_SERVER_AUTH' in tobj: > + if tobj['CKA_TRUST_SERVER_AUTH'] =3D=3D 'CKT_NSS_NOT_TRUST= ED': > + is_distrusted =3D True > + elif tobj['CKA_TRUST_SERVER_AUTH'] =3D=3D 'CKT_NSS_TRUSTED= _DELEGATOR': > + has_server_trust =3D True > + > + if 'CKA_TRUST_EMAIL_PROTECTION' in tobj: > + if tobj['CKA_TRUST_EMAIL_PROTECTION'] =3D=3D 'CKT_NSS_NOT_= TRUSTED': > + is_distrusted =3D True > + elif tobj['CKA_TRUST_EMAIL_PROTECTION'] =3D=3D 'CKT_NSS_TR= USTED_DELEGATOR': > + has_email_trust =3D True > + > + if 'CKA_TRUST_CODE_SIGNING' in tobj: > + if tobj['CKA_TRUST_CODE_SIGNING'] =3D=3D 'CKT_NSS_NOT_TRUS= TED': > + is_distrusted =3D True > + elif tobj['CKA_TRUST_CODE_SIGNING'] =3D=3D 'CKT_NSS_TRUSTE= D_DELEGATOR': > + has_code_trust =3D True > + > + if is_distrusted: > + trust_ext_oid =3D "1.3.6.1.4.1.3319.6.10.1" > + trust_ext_value =3D "0.%06%0a%2b%06%01%04%01%99w%06%0a%01%= 04 0%1e%06%08%2b%06%01%05%05%07%03%04%06%08%2b%06%01%05%05%07%03%01%06%08%2b%= 06%01%05%05%07%03%03" > + write_cert_ext_to_file(f, trust_ext_oid, trust_ext_value, = pk) > + > + trust_ext_oid =3D "2.5.29.37" > + if has_server_trust: > + if has_email_trust: > + if has_code_trust: > + # server + email + code > + trust_ext_value =3D "0%2a%06%03U%1d%25%01%01%ff%04= 0%1e%06%08%2b%06%01%05%05%07%03%04%06%08%2b%06%01%05%05%07%03%01%06%08%2b%06= %01%05%05%07%03%03" > + else: > + # server + email > + trust_ext_value =3D "0 %06%03U%1d%25%01%01%ff%04%1= 60%14%06%08%2b%06%01%05%05%07%03%04%06%08%2b%06%01%05%05%07%03%01" > + else: > + if has_code_trust: > + # server + code > + trust_ext_value =3D "0 %06%03U%1d%25%01%01%ff%04%1= 60%14%06%08%2b%06%01%05%05%07%03%01%06%08%2b%06%01%05%05%07%03%03" > + else: > + # server > + trust_ext_value =3D "0%16%06%03U%1d%25%01%01%ff%04= %0c0%0a%06%08%2b%06%01%05%05%07%03%01" > + else: > + if has_email_trust: > + if has_code_trust: > + # email + code > + trust_ext_value =3D "0 %06%03U%1d%25%01%01%ff%04%1= 60%14%06%08%2b%06%01%05%05%07%03%04%06%08%2b%06%01%05%05%07%03%03" > + else: > + # email > + trust_ext_value =3D "0%16%06%03U%1d%25%01%01%ff%04= %0c0%0a%06%08%2b%06%01%05%05%07%03%04" > + else: > + if has_code_trust: > + # code > + trust_ext_value =3D "0%16%06%03U%1d%25%01%01%ff%04= %0c0%0a%06%08%2b%06%01%05%05%07%03%03" > + else: > + # none > + trust_ext_value =3D "0%18%06%03U%1d%25%01%01%ff%04= %0e0%0c%06%0a%2b%06%01%04%01%99w%06%0a%10" > + > + # no 2.5.29.37 for neutral certificates > + if (is_distrusted or has_server_trust or has_email_trust or ha= s_code_trust): > + write_cert_ext_to_file(f, trust_ext_oid, trust_ext_value, = pk) > + > + pk =3D '' > + f.write("\n") > + > + f.write("[p11-kit-object-v1]\n") > + f.write("label: "); > + f.write(tobj['CKA_LABEL']) > + f.write("\n") > + if is_distrusted: > + f.write("x-distrusted: true\n") > + elif has_server_trust or has_email_trust or has_code_trust: > + f.write("trusted: true\n") > + else: > + f.write("trusted: false\n") > + > + # requires p11-kit >=3D 0.23.4 > + f.write("nss-mozilla-ca-policy: true\n") > + f.write("modifiable: false\n"); > + > + # requires p11-kit >=3D 0.23.19 > + for t in list(cert_distrust_types.keys()): > + if t in obj: > + value =3D obj[t] > + if value =3D=3D 'CK_FALSE': > + value =3D bytearray(1) > + f.write(cert_distrust_types[t] + ": \"") > + f.write(urllib.parse.quote(value)); > + f.write("\"\n") > + > f.write("-----BEGIN CERTIFICATE-----\n") > - f.write("\n".join(textwrap.wrap(base64.b64encode(obj['CKA_VALU= E']), 64))) > + temp_encoded_b64 =3D base64.b64encode(obj['CKA_VALUE']) > + temp_wrapped =3D textwrap.wrap(temp_encoded_b64.decode(), 64) > + f.write("\n".join(temp_wrapped)) > f.write("\n-----END CERTIFICATE-----\n") > + f.write(cert_comment) > + f.write("\n") > + > else: > f.write("[p11-kit-object-v1]\n") > f.write("label: "); > @@ -230,14 +399,15 @@ for tobj in objects: > f.write("\n") > f.write("class: certificate\n") > f.write("certificate-type: x-509\n") > + f.write("modifiable: false\n"); > f.write("issuer: \""); > - f.write(urllib.quote(tobj['CKA_ISSUER'])); > + f.write(urllib.parse.quote(tobj['CKA_ISSUER'])); > f.write("\"\n") > f.write("serial-number: \""); > - f.write(urllib.quote(tobj['CKA_SERIAL_NUMBER'])); > + f.write(urllib.parse.quote(tobj['CKA_SERIAL_NUMBER'])); > f.write("\"\n") > if (tobj['CKA_TRUST_SERVER_AUTH'] =3D=3D 'CKT_NSS_NOT_TRUSTED')= or (tobj['CKA_TRUST_EMAIL_PROTECTION'] =3D=3D 'CKT_NSS_NOT_TRUSTED') or (tob= j['CKA_TRUST_CODE_SIGNING'] =3D=3D 'CKT_NSS_NOT_TRUSTED'): > f.write("x-distrusted: true\n") > f.write("\n\n") > f.close() > - print " -> written as '%s', trust =3D %s, openssl-trust =3D %s, di= strust =3D %s, openssl-distrust =3D %s" % (fname, trustbits, openssl_trustfla= gs, distrustbits, openssl_distrustflags) > + print(" -> written as '%s', trust =3D %s, openssl-trust =3D %s, di= strust =3D %s, openssl-distrust =3D %s" % (fname, trustbits, openssl_trustfla= gs, distrustbits, openssl_distrustflags)) > diff --git a/config/rootfiles/common/ca-certificates b/config/rootfiles/com= mon/ca-certificates > index 087c3e450..06eb66f3b 100644 > --- a/config/rootfiles/common/ca-certificates > +++ b/config/rootfiles/common/ca-certificates > @@ -1,4 +1,7 @@ > +#etc/pki > +#etc/pki/ca-trust > +#etc/pki/ca-trust/source > +etc/pki/ca-trust/source/ca-bundle.trust.p11-kit > etc/ssl/cert.pem > -#etc/ssl/certs > etc/ssl/certs/ca-bundle.crt > etc/ssl/certs/ca-bundle.trust.crt > diff --git a/lfs/ca-certificates b/lfs/ca-certificates > index f3c68a7c0..9e37687da 100644 > --- a/lfs/ca-certificates > +++ b/lfs/ca-certificates > @@ -24,7 +24,7 @@ >=20 > include Config >=20 > -VER =3D 20210611 > +VER =3D 20210819 >=20 > THISAPP =3D ca-certificates > DIR_APP =3D $(DIR_SRC)/$(THISAPP) > --=20 > 2.33.0 >=20 --===============6921806315604488173==--