summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xguix/scripts/substitute.scm115
-rw-r--r--tests/substitute.scm20
2 files changed, 100 insertions, 35 deletions
diff --git a/guix/scripts/substitute.scm b/guix/scripts/substitute.scm
index 455f5a23461..49f5389fe27 100755
--- a/guix/scripts/substitute.scm
+++ b/guix/scripts/substitute.scm
@@ -58,6 +58,7 @@
58 #:use-module (web response) 58 #:use-module (web response)
59 #:use-module (guix http-client) 59 #:use-module (guix http-client)
60 #:export (%allow-unauthenticated-substitutes? 60 #:export (%allow-unauthenticated-substitutes?
61 %allow-unsafe-substitute-uris?
61 %reply-file-descriptor 62 %reply-file-descriptor
62 63
63 substitute-urls 64 substitute-urls
@@ -90,6 +91,19 @@ disabled!~%"))
90 (and=> (getenv "GUIX_ALLOW_UNAUTHENTICATED_SUBSTITUTES") 91 (and=> (getenv "GUIX_ALLOW_UNAUTHENTICATED_SUBSTITUTES")
91 (cut string-ci=? <> "yes")))) 92 (cut string-ci=? <> "yes"))))
92 93
94(define %allow-unsafe-substitute-uris?
95 ;; Whether to allow substitutes with a "file://" URI. These are useful for
96 ;; testing purposes but should never be allowed otherwise, as the URIs
97 ;; contained in narinfos are not signed and could point to any
98 ;; attacker-controlled location. Use GUIX_ALLOW_UNAUTHENTICATED_SUBSTITUTES
99 ;; as a way to tell when we're testing.
100 (make-parameter (%allow-unauthenticated-substitutes?)))
101
102(define %allow-unsafe-narinfo-uris?
103 ;; Like %allow-unsafe-substitute-uris?, but affecting the urls used to fetch
104 ;; narinfos.
105 (make-parameter (%allow-unauthenticated-substitutes?)))
106
93(define (at-most max-length lst) 107(define (at-most max-length lst)
94 "If LST is shorter than MAX-LENGTH, return it and the empty list; otherwise 108 "If LST is shorter than MAX-LENGTH, return it and the empty list; otherwise
95return its MAX-LENGTH first elements and its tail." 109return its MAX-LENGTH first elements and its tail."
@@ -368,6 +382,21 @@ server certificates."
368 (drain-input socket) 382 (drain-input socket)
369 socket)))))))) 383 socket))))))))
370 384
385(define (uri-safe? uri)
386 "Return a boolean indicating whether URI, which is either a uri or its
387string representation, is generally safe to use without requiring any trust."
388 (let ((uri (if (uri? uri)
389 uri
390 (string->uri uri))))
391 (and uri
392 (case (uri-scheme uri)
393 ((#f file) #f)
394 (else #t)))))
395
396(define (narinfo-uris-safe? narinfo)
397 (or (%allow-unsafe-substitute-uris?)
398 (every uri-safe? (narinfo-uris narinfo))))
399
371(define* (process-substitution/fallback narinfo destination 400(define* (process-substitution/fallback narinfo destination
372 #:key cache-urls acl 401 #:key cache-urls acl
373 deduplicate? print-build-trace? 402 deduplicate? print-build-trace?
@@ -391,9 +420,10 @@ way to download the nar."
391 #:open-connection 420 #:open-connection
392 open-connection-for-uri/cached) 421 open-connection-for-uri/cached)
393 ((alternate) 422 ((alternate)
394 (if (or (equivalent-narinfo? narinfo alternate) 423 (if (and (narinfo-uris-safe? alternate)
395 (valid-narinfo? alternate acl) 424 (or (equivalent-narinfo? narinfo alternate)
396 (%allow-unauthenticated-substitutes?)) 425 (valid-narinfo? alternate acl)
426 (%allow-unauthenticated-substitutes?)))
397 (guard (c ((or (http-get-error? c) 427 (guard (c ((or (http-get-error? c)
398 (network-error? c)) 428 (network-error? c))
399 (when (http-get-error? c) 429 (when (http-get-error? c)
@@ -429,6 +459,16 @@ PORT."
429 (const #t) 459 (const #t)
430 (cut valid-narinfo? <> acl)))) 460 (cut valid-narinfo? <> acl))))
431 461
462 (define (fallback)
463 (process-substitution/fallback narinfo destination
464 #:cache-urls cache-urls
465 #:acl acl
466 #:deduplicate? deduplicate?
467 #:print-build-trace?
468 print-build-trace?
469 #:fast-decompression?
470 fast-decompression?))
471
432 (unless narinfo 472 (unless narinfo
433 (raise 473 (raise
434 (formatted-message 474 (formatted-message
@@ -437,33 +477,30 @@ PORT."
437 477
438 (let ((expected-hash 478 (let ((expected-hash
439 actual-hash 479 actual-hash
440 (guard 480 (cond
441 (c ((or (http-get-error? c) 481 ((narinfo-uris-safe? narinfo)
442 (network-error? c)) 482 (guard
443 (when (http-get-error? c) 483 (c ((or (http-get-error? c)
444 (warning (G_ "download from '~a' failed: ~a, ~s~%") 484 (network-error? c))
445 (uri->string (http-get-error-uri c)) 485 (when (http-get-error? c)
446 (http-get-error-code c) 486 (warning (G_ "download from '~a' failed: ~a, ~s~%")
447 (http-get-error-reason c))) 487 (uri->string (http-get-error-uri c))
448 (format 488 (http-get-error-code c)
449 (current-error-port) 489 (http-get-error-reason c)))
450 (G_ "retrying download of '~a' with other substitute URLs...~%") 490 (format
451 store-item) 491 (current-error-port)
452 (process-substitution/fallback narinfo destination 492 (G_ "retrying download of '~a' with other substitute URLs...~%")
453 #:cache-urls cache-urls 493 store-item)
454 #:acl acl 494 (fallback)))
455 #:deduplicate? deduplicate? 495 (download-nar narinfo destination
456 #:print-build-trace? 496 #:deduplicate? deduplicate?
457 print-build-trace? 497 #:print-build-trace? print-build-trace?
458 #:fast-decompression? 498 #:fast-decompression? fast-decompression?
459 fast-decompression?))) 499 #:open-connection-for-uri
460 (download-nar narinfo destination 500 open-connection-for-uri/cached
461 #:deduplicate? deduplicate? 501 #:keep-alive? #t)))
462 #:print-build-trace? print-build-trace? 502 (else
463 #:fast-decompression? fast-decompression? 503 (fallback)))))
464 #:open-connection-for-uri
465 open-connection-for-uri/cached
466 #:keep-alive? #t))))
467 (values narinfo 504 (values narinfo
468 expected-hash 505 expected-hash
469 actual-hash))) 506 actual-hash)))
@@ -519,10 +556,17 @@ substitutes may be unavailable\n")))))
519found." 556found."
520 (assoc-ref (force options) option)))) 557 (assoc-ref (force options) option))))
521 558
559(define (assert-safe-uris uris)
560 (unless (or (%allow-unsafe-narinfo-uris?)
561 (every uri-safe? uris))
562 (leave (G_ "unsafe or invalid URI in ~S~%") uris))
563 uris)
564
522(define %default-substitute-urls 565(define %default-substitute-urls
523 (match (and=> (or (find-daemon-option "untrusted-substitute-urls") ;client 566 (match (or (and=> (find-daemon-option "untrusted-substitute-urls") ;client
524 (find-daemon-option "substitute-urls")) ;admin 567 (compose assert-safe-uris string-tokenize))
525 string-tokenize) 568 (and=> (find-daemon-option "substitute-urls") ;admin
569 string-tokenize))
526 ((urls ...) 570 ((urls ...)
527 urls) 571 urls)
528 (#f 572 (#f
@@ -558,8 +602,9 @@ is shorter than MAX elements, then it is directly returned."
558 (let* ((option (find-daemon-option "discover")) 602 (let* ((option (find-daemon-option "discover"))
559 (discover? (and option (string=? option "true")))) 603 (discover? (and option (string=? option "true"))))
560 (if discover? 604 (if discover?
561 (randomize-substitute-urls (read-substitute-urls)) 605 (randomize-substitute-urls (filter uri-safe?
562 '()))) 606 (read-substitute-urls)))
607 '())))
563 608
564(define substitute-urls 609(define substitute-urls
565 ;; List of substitute URLs. 610 ;; List of substitute URLs.
diff --git a/tests/substitute.scm b/tests/substitute.scm
index cd75d56b981..10b74db084e 100644
--- a/tests/substitute.scm
+++ b/tests/substitute.scm
@@ -206,6 +206,26 @@ a file for NARINFO."
206;; daemon. 206;; daemon.
207(%reply-file-descriptor #f) 207(%reply-file-descriptor #f)
208 208
209(test-equal "file:// URI prohibited by default"
210 "not-found\n"
211 (with-output-to-string
212 (lambda ()
213 (%allow-unsafe-substitute-uris? #f)
214 (let ((narinfo %narinfo))
215 (with-narinfo (string-append narinfo "Signature: "
216 (signature-field narinfo) "\n")
217 (call-with-temporary-directory
218 (lambda (directory)
219 (with-input-from-string (string-append
220 "substitute " (%store-prefix)
221 "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo "
222 directory "/shouldnotbewritten\n")
223 (lambda ()
224 (guix-substitute "--substitute"))))))))))
225
226;; Allow these for ease of testing for the rest of the tests
227(%allow-unsafe-substitute-uris? #t)
228
209 229
210(test-equal "query narinfo without signature" 230(test-equal "query narinfo without signature"
211 "" ; not substitutable 231 "" ; not substitutable