fukamachi / qlot

A project-local library installer for Common Lisp
https://qlot.tech
MIT License
464 stars 39 forks source link

A failure in the SSL library occurred: X509_V_ERR_CERT_HAS_EXPIRED #234

Closed braunse closed 1 month ago

braunse commented 8 months ago

Describe the bug

When I try to use the Ultralisp dist in my qlfile, the qlot install command fails on my WSL2 system with the following error message:

Installing Quicklisp to /home/sebastien/dev/repro/.qlot/...
Reading '/home/sebastien/dev/repro/qlfile'...
● [1/2] quicklisp  Installing the new dist.
⨯ [2/2] ultralisp  Failed to install

Unexpected error: A failure in the SSL library occurred on handle #.(SB-SYS:INT-SAP #X7F3ADC0EBE80) (SSL_get_error: 1). ERR_p:
SSL_get_verify_result: 10 X509_V_ERR_CERT_HAS_EXPIRED
This can be a bug of Qlot.
Report it at https://github.com/fukamachi/qlot/issues/new/choose.
Please attach the stack trace dumped to '/tmp/qlot-error-5GEXGEGC.log'.

I can access https://dist.ultralisp.org with curl with no issues, confirming that the certificate has not, in fact, expired:

Curl transcript
* Host dist.ultralisp.org:443 was resolved.
* IPv6: 2a06:98c1:3120::3, 2a06:98c1:3121::3
* IPv4: 188.114.97.3, 188.114.96.3
*   Trying 188.114.97.3:443...
* Connected to dist.ultralisp.org (188.114.97.3) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / id-ecPublicKey
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=ultralisp.org
*  start date: Jan  3 17:46:09 2024 GMT
*  expire date: Apr  2 17:46:08 2024 GMT
*  subjectAltName: host "dist.ultralisp.org" matched cert's "*.ultralisp.org"
*  issuer: C=US; O=Let's Encrypt; CN=E1
*  SSL certificate verify ok.
*   Certificate level 0: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA384
*   Certificate level 1: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA384
*   Certificate level 2: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA384
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://dist.ultralisp.org/
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: dist.ultralisp.org]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.6.0]
* [HTTP/2] [1] [accept: */*]
> GET / HTTP/2
> Host: dist.ultralisp.org
> User-Agent: curl/8.6.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/2 200
< date: Tue, 20 Feb 2024 08:07:58 GMT
< content-type: binary/octet-stream
< content-length: 355
< x-amz-id-2: Vy9l3Rm1kPYX8d4c24lfOHwzwY5qD44QXVJx+fqljl5HbFj44twRAOqhwNmWue9pmEYMnlQhQmQ=
< x-amz-request-id: J7G0XFMTNX98JH1N
< last-modified: Tue, 20 Feb 2024 06:50:30 GMT
< etag: "f60d3c00338be05e8d49308769ec419f"
< cf-cache-status: DYNAMIC
< report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=wlKCRi68Ww2vxsASgGS5qftXdKMuP6DohSSwWneYcDisUDE8ZiMVhXTdn3IqYvyfLGMmFYVBZJGpbnArplcPDtcNCtZa1uV2ogzoyRVQfDKrxfV9aTgQZdiOjOSZ3OWxLkT06Io%3D"}],"group":"cf-nel","max_age":604800}
< nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
< server: cloudflare
< cf-ray: 8585424ed8362a5b-CDG
< alt-svc: h3=":443"; ma=86400
<
name: ultralisp
version: 20240220062003
distinfo-subscription-url: http://dist.ultralisp.org/ultralisp.txt
distinfo-template-url: http://dist.ultralisp.org/ultralisp/{{version}}/distinfo.txt
release-index-url: http://dist.ultralisp.org/ultralisp/20240220062003/releases.txt
system-index-url: http://dist.ultralisp.org/ultralisp/20240220062003/systems.txt
* Connection #0 to host dist.ultralisp.org left intact

I have also verified that the system clock is set correctly.

The same error does not happen for me on a bare-metal installation of the same base operating system.

Reproducible steps

The following bash commands reproduce the failure on my system.

mkdir repro
cd repro
echo "dist ultralisp https://dist.ultralisp.org" > qlfile
qlot install

Error messages

Stack Trace
Backtrace for: #
0: ((LAMBDA NIL :IN UIOP/IMAGE:PRINT-BACKTRACE))
1: ((FLET "THUNK" :IN UIOP/STREAM:CALL-WITH-SAFE-IO-SYNTAX))
2: (SB-IMPL::%WITH-STANDARD-IO-SYNTAX #)
3: (UIOP/STREAM:CALL-WITH-SAFE-IO-SYNTAX # :PACKAGE :CL)
4: (UIOP/IMAGE:PRINT-CONDITION-BACKTRACE # :STREAM # :COUNT NIL)
5: ((FLET "BEFORE29" :IN QLOT/CLI:QLOT-COMMAND) # #P"/tmp/qlot-error-5GEXGEGE.log")
6: (UIOP/STREAM:CALL-WITH-TEMPORARY-FILE # :WANT-STREAM-P T :WANT-PATHNAME-P T :DIRECTION :OUTPUT :KEEP T :AFTER NIL :DIRECTORY NIL :TYPE "log" :PREFIX "qlot-error-" :SUFFIX "" :ELEMENT-TYPE NIL :EXTERNAL-FORMAT NIL)
7: ((LAMBDA (QLOT/CLI::C) :IN QLOT/CLI:QLOT-COMMAND) #)
8: (SB-KERNEL::%SIGNAL #)
9: (ERROR #)
10: (LPARALLEL.KERNEL-UTIL::RECEIVE-RESULTS #S(LPARALLEL.KERNEL:CHANNEL :QUEUE #S(LPARALLEL.CONS-QUEUE:CONS-QUEUE :IMPL #S(LPARALLEL.RAW-QUEUE:RAW-QUEUE :HEAD NIL :TAIL NIL) :LOCK # :CVAR #) :KERNEL #) # #)
11: (LPARALLEL.COGNATE::PMAP-ITERATE # # ((# #)) 2 4)
12: (LPARALLEL.COGNATE:PMAPC # (# #))
13: (QLOT/PROGRESS:RUN-IN-PARALLEL # (# #) :CONCURRENCY 4 :JOB-HEADER-FN # :FAILED-FN #)
14: (QLOT/INSTALL::APPLY-QLFILE-TO-QLHOME #P"/home/sebastien/dev/repro/qlfile" #P"/home/sebastien/dev/repro/.qlot/" :IGNORE-LOCK NIL :PROJECTS NIL :CACHE-DIRECTORY NIL :CONCURRENCY NIL)
15: (QLOT/INSTALL:INSTALL-QLFILE #P"/home/sebastien/dev/repro/qlfile" :QUICKLISP-HOME NIL :INSTALL-DEPS T :CACHE-DIRECTORY NIL :CONCURRENCY NIL)
16: (QLOT/INSTALL:INSTALL-PROJECT #P"/home/sebastien/dev/repro/" :INSTALL-DEPS T :CACHE-DIRECTORY NIL :CONCURRENCY NIL :INIT NIL)
17: (UIOP/FILESYSTEM:CALL-WITH-CURRENT-DIRECTORY #P"/home/sebastien/dev/repro/" #)
18: (QLOT/CLI:QLOT-COMMAND "install")
19: (SB-INT:SIMPLE-EVAL-IN-LEXENV (APPLY (QUOTE ROS/SCRIPT/QLOT::MAIN) ROSWELL:*ARGV*) #)
20: (SB-INT:SIMPLE-EVAL-IN-LEXENV (ROSWELL:QUIT (APPLY (QUOTE ROS/SCRIPT/QLOT::MAIN) ROSWELL:*ARGV*)) #)
21: (SB-EXT:EVAL-TLF (ROSWELL:QUIT (APPLY (QUOTE ROS/SCRIPT/QLOT::MAIN) ROSWELL:*ARGV*)) NIL NIL)
22: ((LABELS SB-FASL::EVAL-FORM :IN SB-INT:LOAD-AS-SOURCE) (ROSWELL:QUIT (APPLY (QUOTE ROS/SCRIPT/QLOT::MAIN) ROSWELL:*ARGV*)) NIL)
23: (SB-INT:LOAD-AS-SOURCE # :VERBOSE NIL :PRINT NIL :CONTEXT "loading")
24: ((LABELS SB-FASL::LOAD-STREAM-1 :IN LOAD) # NIL)
25: (SB-FASL::CALL-WITH-LOAD-BINDINGS # # NIL #)
26: (LOAD # :VERBOSE NIL :PRINT NIL :IF-DOES-NOT-EXIST :ERROR :EXTERNAL-FORMAT :DEFAULT)
27: ((FLET ROSWELL::BODY :IN ROSWELL:SCRIPT) #)
28: (ROSWELL:SCRIPT "/home/sebastien/.roswell/bin/qlot" "install")
29: (ROSWELL:RUN ((:EVAL "(ros:quicklisp)") (:SCRIPT "/home/sebastien/.roswell/bin/qlot" "install") (:QUIT NIL)))
30: (SB-INT:SIMPLE-EVAL-IN-LEXENV (ROSWELL:RUN (QUOTE ((:EVAL "(ros:quicklisp)") (:SCRIPT "/home/sebastien/.roswell/bin/qlot" "install") (:QUIT NIL)))) #)
31: (EVAL (ROSWELL:RUN (QUOTE ((:EVAL "(ros:quicklisp)") (:SCRIPT "/home/sebastien/.roswell/bin/qlot" "install") (:QUIT NIL)))))
32: (SB-IMPL::PROCESS-EVAL/LOAD-OPTIONS ((:EVAL . "(progn #-ros.init(cl:load \"/home/sebastien/lib/roswell/etc/roswell/init.lisp\"))") (:EVAL . "(ros:run '((:eval\"(ros:quicklisp)\")(:script \"/home/sebastien/.roswell/bin/qlot\"\"install\")(:quit ())))")))
33: (SB-IMPL::TOPLEVEL-INIT)
34: ((FLET SB-UNIX::BODY :IN SB-IMPL::START-LISP))
35: ((FLET "WITHOUT-INTERRUPTS-BODY-3" :IN SB-IMPL::START-LISP))
36: (SB-IMPL::%START-LISP)
Above backtrace due to this condition:
A failure in the SSL library occurred on handle # (SSL_get_error: 1). ERR_print_errors():
 2:error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed:s3_clnt.c:1268:
SSL_get_verify_result: 10 X509_V_ERR_CERT_HAS_EXPIRED

Versions:

I can also reproduce the error with Qlot 1.5.0 installed from source with SBCL 2.4.1 installed from system packages.

fukamachi commented 8 months ago

Can you try the following to see if it's from certificates in Dexador?

$ ros -s dexador -e '(print (nth-value 1 (dex:get "https://dist.ultralisp.org/")))'
braunse commented 8 months ago

Thank you for the quick answer. I looked a little deeper into it and found that it is actually related to the certificate bundle in Dexador. I was able to fix it by symlinking ~/.roswell/lisp/quicklisp/dists/quicklisp/software/dexador-20231021-git/certs/cacert.pem to the system-wide certificate bundle /etc/ssl/ca-bundle.pem.

When I undo this fix, I indeed get the same error:

$ ros -s dexador -e '(print (nth-value 1 (dex:get "https://dist.ultralisp.org")))'

debugger invoked on a CL+SSL::SSL-ERROR-SSL in thread
#<THREAD tid=3133 "main thread" RUNNING {10014A0003}>:
  A failure in the SSL library occurred on handle #.(SB-SYS:INT-SAP #X0142A920) (SSL_get_error: 1). ERR_print_errors(): 1:error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed:s3_clnt.c:1268:
SSL_get_verify_result: 10 X509_V_ERR_CERT_HAS_EXPIRED

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [RETRY-REQUEST ] Retry the same request.
  1: [RETRY-INSECURE] Retry the same request without checking for SSL certificate validity.
  2: [CONTINUE      ] Ignore runtime option --eval "(ros:run '((:eval\"(ros:quicklisp)\")(:system \"dexador\")(:eval \"(print (nth-value 1 (dex:get \\\"https://dist.ultralisp.org\\\")))\")(:hook)(:quit)))".
  3: [ABORT         ] Skip rest of --eval and --load options.
  4:                  Skip to toplevel READ/EVAL/PRINT loop.
  5: [EXIT          ] Exit SBCL (calling #'EXIT, killing the process).

(CL+SSL::SSL-SIGNAL-ERROR #.(SB-SYS:INT-SAP #X0142A920) #<FUNCTION CL+SSL::SSL-CONNECT> 1 -1)
   source: (ERROR
            (CASE ERROR-CODE
              ("#.+SSL-ERROR-NONE+" 'SSL-ERROR-NONE)
              ("#.+SSL-ERROR-SSL+" 'SSL-ERROR-SSL)
              ("#.+SSL-ERROR-WANT-READ+" 'SSL-ERROR-WANT-READ)
              ("#.+SSL-ERROR-WANT-WRITE+" 'SSL-ERROR-WANT-WRITE)
              ("#.+SSL-ERROR-WANT-X509-LOOKUP+" 'SSL-ERROR-WANT-X509-LOOKUP)
              ("#.+SSL-ERROR-ZERO-RETURN+" 'SSL-ERROR-ZERO-RETURN)
              ("#.+SSL-ERROR-WANT-CONNECT+" 'SSL-ERROR-WANT-CONNECT)
              ("#.+SSL-ERROR-SYSCALL+" 'SSL-ERROR-ZERO-RETURN)
              (T 'SSL-ERROR/HANDLE))
            :HANDLE HANDLE :RET ERROR-CODE :PRINTED-QUEUE PRINTED-QUEUE :QUEUE
            QUEUE)
0] 5

I would have expected Qlot and/or Dexador to default to the system-wide certificate store, and to honor the $SSL_CERT_DIR and $SSL_CERT_FILE environment variables. Is there any switch or configuration knob to tell Qlot which certificates to trust? I did not see anything in the Qlot source, but I'm not familiar with it.

gigal00p commented 3 months ago

I would have expected Qlot and/or Dexador to default to the system-wide certificate store, and to honor the $SSL_CERT_DIR and $SSL_CERT_FILE environment variables.

Hmm. Apparently it does. Looks like removing ~/.roswell/lisp/quicklisp/dists/quicklisp/software/dexador-20231021-git/certs/cacert.pem makes dexador to fall back to system cert store.

fukamachi commented 1 month ago

This probably had fixed in https://github.com/fukamachi/dexador/pull/179.