mirror of
git://git.gnupg.org/gnupg.git
synced 2024-11-10 21:38:50 +01:00
44c17bcb00
* g10/trustdb.c (tdb_utks): New function. * g10/tofu.c (check_utks): New function. (initdb): Call it. * tests/openpgp/tofu.scm: Modify test to check the effective policy of keys whose effective policy changes when we change the set of UTKs. -- Signed-off-by: Neal H. Walfield <neal@g10code.com> If the set of ultimately trusted keys changes, then it is possible that a binding's effective policy changes. To deal with this, we detect when the set of ultimately trusted keys changes and invalidate all cached policies.
343 lines
11 KiB
Scheme
Executable File
343 lines
11 KiB
Scheme
Executable File
#!/usr/bin/env gpgscm
|
|
|
|
;; Copyright (C) 2016 g10 Code GmbH
|
|
;;
|
|
;; This file is part of GnuPG.
|
|
;;
|
|
;; GnuPG is free software; you can redistribute it and/or modify
|
|
;; it under the terms of the GNU General Public License as published by
|
|
;; the Free Software Foundation; either version 3 of the License, or
|
|
;; (at your option) any later version.
|
|
;;
|
|
;; GnuPG is distributed in the hope that it will be useful,
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
;; GNU General Public License for more details.
|
|
;;
|
|
;; You should have received a copy of the GNU General Public License
|
|
;; along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
(load (with-path "defs.scm"))
|
|
(setup-environment)
|
|
|
|
;; Redefine GPG without --always-trust and a fixed time.
|
|
(define GPG `(,(tool 'gpg) --no-permission-warning
|
|
--faked-system-time=1466684990))
|
|
(define GNUPGHOME (getenv "GNUPGHOME"))
|
|
(if (string=? "" GNUPGHOME)
|
|
(error "GNUPGHOME not set"))
|
|
|
|
(catch (skip "Tofu not supported")
|
|
(call-check `(,@GPG --trust-model=tofu --list-config)))
|
|
|
|
(define KEYS '("2183839A" "BC15C85A" "EE37CF96"))
|
|
|
|
;; Import the test keys.
|
|
(call-check `(,@GPG --import ,(in-srcdir "tofu-keys.asc")))
|
|
|
|
;; Make sure the keys are imported.
|
|
(for-each (lambda (keyid)
|
|
(catch (error "Missing key" keyid)
|
|
(call-check `(,@GPG --list-keys ,keyid))))
|
|
KEYS)
|
|
|
|
;; Get tofu policy for KEYID. Any remaining arguments are simply
|
|
;; passed to GPG.
|
|
;;
|
|
;; This function only supports keys with a single user id.
|
|
(define (getpolicy keyid . args)
|
|
(let ((policy
|
|
(list-ref (assoc "tfs" (gpg-with-colons
|
|
`(--trust-model=tofu --with-tofu-info
|
|
,@args
|
|
--list-keys ,keyid))) 5)))
|
|
(unless (member policy '("auto" "good" "unknown" "bad" "ask"))
|
|
(error "Bad policy:" policy))
|
|
policy))
|
|
|
|
;; Check that KEYID's tofu policy matches EXPECTED-POLICY. Any
|
|
;; remaining arguments are simply passed to GPG.
|
|
;;
|
|
;; This function only supports keys with a single user id.
|
|
(define (checkpolicy keyid expected-policy . args)
|
|
(let ((policy (apply getpolicy `(,keyid ,@args))))
|
|
(unless (string=? policy expected-policy)
|
|
(error keyid ": Expected policy to be" expected-policy
|
|
"but got" policy))))
|
|
|
|
;; Get the trust level for KEYID. Any remaining arguments are simply
|
|
;; passed to GPG.
|
|
;;
|
|
;; This function only supports keys with a single user id.
|
|
(define (gettrust keyid . args)
|
|
(let ((trust
|
|
(list-ref (assoc "pub" (gpg-with-colons
|
|
`(--trust-model=tofu
|
|
,@args
|
|
--list-keys ,keyid))) 1)))
|
|
(unless (and (= 1 (string-length trust))
|
|
(member (string-ref trust 0) (string->list "oidreqnmfuws-")))
|
|
(error "Bad trust value:" trust))
|
|
trust))
|
|
|
|
;; Check that KEYID's trust level matches EXPECTED-TRUST. Any
|
|
;; remaining arguments are simply passed to GPG.
|
|
;;
|
|
;; This function only supports keys with a single user id.
|
|
(define (checktrust keyid expected-trust . args)
|
|
(let ((trust (apply gettrust `(,keyid ,@args))))
|
|
(unless (string=? trust expected-trust)
|
|
(error keyid ": Expected trust to be" expected-trust
|
|
"but got" trust))))
|
|
|
|
;; Set key KEYID's policy to POLICY. Any remaining arguments are
|
|
;; passed as options to gpg.
|
|
(define (setpolicy keyid policy . args)
|
|
(call-check `(,@GPG --trust-model=tofu ,@args
|
|
--tofu-policy ,policy ,keyid)))
|
|
|
|
(info "Checking tofu policies and trust...")
|
|
|
|
;; Carefully remove the TOFU db.
|
|
(catch '() (unlink (string-append GNUPGHOME "/tofu.db")))
|
|
|
|
;; Verify a message. There should be no conflict and the trust
|
|
;; policy should be set to auto.
|
|
(call-check `(,@GPG --trust-model=tofu
|
|
--verify ,(in-srcdir "tofu-2183839A-1.txt")))
|
|
|
|
(checkpolicy "2183839A" "auto")
|
|
;; Check default trust.
|
|
(checktrust "2183839A" "m")
|
|
|
|
;; Trust should be derived lazily. Thus, if the policy is set to
|
|
;; auto and we change --tofu-default-policy, then the trust should
|
|
;; change as well. Try it.
|
|
(checktrust "2183839A" "f" '--tofu-default-policy=good)
|
|
(checktrust "2183839A" "-" '--tofu-default-policy=unknown)
|
|
(checktrust "2183839A" "n" '--tofu-default-policy=bad)
|
|
|
|
;; Change the policy to something other than auto and make sure the
|
|
;; policy and the trust are correct.
|
|
(for-each-p
|
|
"Setting a fixed policy..."
|
|
(lambda (policy)
|
|
(let ((expected-trust
|
|
(cond
|
|
((string=? "good" policy) "f")
|
|
((string=? "unknown" policy) "-")
|
|
(else "n"))))
|
|
(setpolicy "2183839A" policy)
|
|
|
|
;; Since we have a fixed policy, the trust level shouldn't
|
|
;; change if we change the default policy.
|
|
(for-each-p
|
|
""
|
|
(lambda (default-policy)
|
|
(checkpolicy "2183839A" policy
|
|
'--tofu-default-policy default-policy)
|
|
(checktrust "2183839A" expected-trust
|
|
'--tofu-default-policy default-policy))
|
|
'("auto" "good" "unknown" "bad" "ask"))))
|
|
'("good" "unknown" "bad"))
|
|
|
|
;; At the end, 2183839A's policy should be bad.
|
|
(checkpolicy "2183839A" "bad")
|
|
|
|
;; BC15C85A and 2183839A conflict. A policy setting of "auto"
|
|
;; (BC15C85A's state) will result in an effective policy of ask. But,
|
|
;; a policy setting of "bad" will result in an effective policy of
|
|
;; bad.
|
|
(setpolicy "BC15C85A" "auto")
|
|
(checkpolicy "BC15C85A" "ask")
|
|
(checkpolicy "2183839A" "bad")
|
|
|
|
;; EE37CF96, 2183839A, and BC15C85A conflict. We change BC15C85A's
|
|
;; policy to auto and leave 2183839A's policy at bad. This conflict
|
|
;; should cause BC15C85A's policy to be changed to ask (since it is
|
|
;; auto), but not affect 2183839A's policy.
|
|
(setpolicy "BC15C85A" "auto")
|
|
(checkpolicy "BC15C85A" "ask")
|
|
(call-check `(,@GPG --trust-model=tofu
|
|
--verify ,(in-srcdir "tofu-EE37CF96-1.txt")))
|
|
(checkpolicy "BC15C85A" "ask")
|
|
(checkpolicy "2183839A" "bad")
|
|
(checkpolicy "EE37CF96" "ask")
|
|
|
|
|
|
|
|
;; Check that we detect the following attack:
|
|
;;
|
|
;; Alice and Bob each have a key and cross sign them. Bob then adds a
|
|
;; new user id, "Alice". TOFU should now detect a conflict, because
|
|
;; Alice only signed Bob's "Bob" user id.
|
|
|
|
(display "Checking cross sigs...\n")
|
|
(define GPG `(,(tool 'gpg) --no-permission-warning
|
|
--faked-system-time=1476304861))
|
|
|
|
;; Carefully remove the TOFU db.
|
|
(catch '() (unlink (string-append GNUPGHOME "/tofu.db")))
|
|
|
|
(define DIR "tofu/cross-sigs")
|
|
;; The test keys.
|
|
(define KEYA "1938C3A0E4674B6C217AC0B987DB2814EC38277E")
|
|
(define KEYB "DC463A16E42F03240D76E8BA8B48C6BD871C2247")
|
|
(define KEYIDA (substring KEYA (- (string-length KEYA) 8)))
|
|
(define KEYIDB (substring KEYB (- (string-length KEYB) 8)))
|
|
|
|
(define (verify-messages)
|
|
(for-each
|
|
(lambda (key)
|
|
(for-each
|
|
(lambda (i)
|
|
(let ((fn (in-srcdir DIR (string-append key "-" i ".txt"))))
|
|
(call-check `(,@GPG --trust-model=tofu --verify ,fn))))
|
|
(list "1" "2")))
|
|
(list KEYIDA KEYIDB)))
|
|
|
|
;; Import the public keys.
|
|
(display " > Two keys. ")
|
|
(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDA "-1.gpg"))))
|
|
(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-1.gpg"))))
|
|
;; Make sure the tofu engine registers the keys.
|
|
(verify-messages)
|
|
(display "<\n")
|
|
|
|
;; Since there is no conflict, the policy should be auto.
|
|
(checkpolicy KEYA "auto")
|
|
(checkpolicy KEYB "auto")
|
|
|
|
;; Import the cross sigs.
|
|
(display " > Adding cross signatures. ")
|
|
(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDA "-2.gpg"))))
|
|
(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-2.gpg"))))
|
|
(verify-messages)
|
|
(display "<\n")
|
|
|
|
;; There is still no conflict, so the policy shouldn't have changed.
|
|
(checkpolicy KEYA "auto")
|
|
(checkpolicy KEYB "auto")
|
|
|
|
;; Import the conflicting user id.
|
|
(display " > Adding conflicting user id. ")
|
|
(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-3.gpg"))))
|
|
(verify-messages)
|
|
(display "<\n")
|
|
|
|
(checkpolicy KEYA "ask")
|
|
(checkpolicy KEYB "ask")
|
|
|
|
;; Import Alice's signature on the conflicting user id. Since there
|
|
;; is now a cross signature, we should revert to the default policy.
|
|
(display " > Adding cross signature on user id. ")
|
|
(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-4.gpg"))))
|
|
(verify-messages)
|
|
(display "<\n")
|
|
|
|
(checkpolicy KEYA "auto")
|
|
(checkpolicy KEYB "auto")
|
|
|
|
;; Remove the keys.
|
|
(call-check `(,@GPG --delete-key ,KEYA))
|
|
(call-check `(,@GPG --delete-key ,KEYB))
|
|
|
|
|
|
;; Check that we detect the following attack:
|
|
;;
|
|
;; Alice has an ultimately trusted key and she signs Bob's key. Then
|
|
;; Bob adds a new user id, "Alice". TOFU should now detect a
|
|
;; conflict, because Alice only signed Bob's "Bob" user id.
|
|
;;
|
|
;;
|
|
;; The Alice key:
|
|
;; pub rsa2048 2016-10-11 [SC]
|
|
;; 1938C3A0E4674B6C217AC0B987DB2814EC38277E
|
|
;; uid [ultimate] Spy Cow <spy@cow.com>
|
|
;; sub rsa2048 2016-10-11 [E]
|
|
;;
|
|
;; The Bob key:
|
|
;;
|
|
;; pub rsa2048 2016-10-11 [SC]
|
|
;; DC463A16E42F03240D76E8BA8B48C6BD871C2247
|
|
;; uid [ full ] Spy R. Cow <spy@cow.com>
|
|
;; uid [ full ] Spy R. Cow <spy@cow.de>
|
|
;; sub rsa2048 2016-10-11 [E]
|
|
|
|
(display "Checking UTK sigs...\n")
|
|
(define GPG `(,(tool 'gpg) --no-permission-warning
|
|
--faked-system-time=1476304861))
|
|
|
|
;; Carefully remove the TOFU db.
|
|
(catch '() (unlink (string-append GNUPGHOME "/tofu.db")))
|
|
|
|
(define DIR "tofu/cross-sigs")
|
|
;; The test keys.
|
|
(define KEYA "1938C3A0E4674B6C217AC0B987DB2814EC38277E")
|
|
(define KEYB "DC463A16E42F03240D76E8BA8B48C6BD871C2247")
|
|
(define KEYIDA (substring KEYA (- (string-length KEYA) 8)))
|
|
(define KEYIDB (substring KEYB (- (string-length KEYB) 8)))
|
|
|
|
(define (verify-messages)
|
|
(for-each
|
|
(lambda (key)
|
|
(for-each
|
|
(lambda (i)
|
|
(let ((fn (in-srcdir DIR (string-append key "-" i ".txt"))))
|
|
(call-check `(,@GPG --trust-model=tofu --verify ,fn))))
|
|
(list "1" "2")))
|
|
(list KEYIDA KEYIDB)))
|
|
|
|
;; Import the public keys.
|
|
(display " > Two keys. ")
|
|
(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDA "-1.gpg"))))
|
|
(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-1.gpg"))))
|
|
(display "<\n")
|
|
|
|
(checkpolicy KEYA "auto")
|
|
(checkpolicy KEYB "auto")
|
|
|
|
;; Import the cross sigs.
|
|
(display " > Adding cross signatures. ")
|
|
(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDA "-2.gpg"))))
|
|
(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-2.gpg"))))
|
|
(display "<\n")
|
|
|
|
(checkpolicy KEYA "auto")
|
|
(checkpolicy KEYB "auto")
|
|
|
|
;; Make KEYA ultimately trusted.
|
|
(display (string-append " > Marking " KEYA " as ultimately trusted. "))
|
|
(pipe:do
|
|
(pipe:echo (string-append KEYA ":6:\n"))
|
|
(pipe:gpg `(--import-ownertrust)))
|
|
(display "<\n")
|
|
|
|
;; An ultimately trusted key's policy is good.
|
|
(checkpolicy KEYA "good")
|
|
;; A key signed by a UTK for which there is no policy gets the default
|
|
;; policy of good.
|
|
(checkpolicy KEYB "good")
|
|
|
|
;; Import the conflicting user id.
|
|
(display " > Adding conflicting user id. ")
|
|
(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-3.gpg"))))
|
|
(verify-messages)
|
|
(display "<\n")
|
|
|
|
(checkpolicy KEYA "good")
|
|
(checkpolicy KEYB "ask")
|
|
|
|
;; Import Alice's signature on the conflicting user id.
|
|
(display " > Adding cross signature on user id. ")
|
|
(call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-4.gpg"))))
|
|
(verify-messages)
|
|
(display "<\n")
|
|
|
|
(checkpolicy KEYA "good")
|
|
(checkpolicy KEYB "good")
|
|
|
|
;; Remove the keys.
|
|
(call-check `(,@GPG --delete-key ,KEYA))
|
|
(call-check `(,@GPG --delete-key ,KEYB))
|