~ruther/guix-local

f82cc5fdbe62d835d884f2be2289c95da478da25 — Ludovic Courtès 12 years ago 554f26e
archive: Add '--authorize'.

* guix/scripts/archive.scm (authorize-key): New procedure.
  (guix-archive): Call it when OPTS contains 'authorize-key'.
* tests/guix-archive.sh: Add test with invalid public key.
* guix/pki.scm: Export '%acl-file'.
* doc/guix.texi (Invoking guix archive): Make it clear that '--import'
  works only with authorized keys.  Document '--authorize'.
4 files changed, 50 insertions(+), 2 deletions(-)

M doc/guix.texi
M guix/pki.scm
M guix/scripts/archive.scm
M tests/guix-archive.sh
M doc/guix.texi => doc/guix.texi +18 -2
@@ 942,7 942,8 @@ Archives are stored in the ``Nix archive'' or ``Nar'' format, which is
comparable in spirit to `tar'.  When exporting, the daemon digitally
signs the contents of the archive, and that digital signature is
appended.  When importing, the daemon verifies the signature and rejects
the import in case of an invalid signature.
the import in case of an invalid signature or if the signing key is not
authorized.
@c FIXME: Add xref to daemon doc about signatures.

The main options are:


@@ 955,9 956,11 @@ resulting archive to the standard output.
@item --import
Read an archive from the standard input, and import the files listed
therein into the store.  Abort if the archive has an invalid digital
signature.
signature, or if it is signed by a public key not among the authorized
keys (see @code{--authorize} below.)

@item --generate-key[=@var{parameters}]
@cindex signing, archives
Generate a new key pair for the daemons.  This is a prerequisite before
archives can be exported with @code{--export}.  Note that this operation
usually takes time, because it needs to gather enough entropy to


@@ 970,6 973,19 @@ is a 4096-bit RSA key.  Alternately, @var{parameters} can specify
@code{genkey} parameters suitable for Libgcrypt (@pxref{General
public-key related Functions, @code{gcry_pk_genkey},, gcrypt, The
Libgcrypt Reference Manual}).

@item --authorize
@cindex authorizing, archives
Authorize imports signed by the public key passed on standard input.
The public key must be in ``s-expression advanced format''---i.e., the
same format as the @file{signing-key.pub} file.

The list of authorized keys is kept in the human-editable file
@file{/etc/guix/acl}.  The file contains
@url{http://people.csail.mit.edu/rivest/Sexp.txt, ``advanced-format
s-expressions''} and is structured as an access-control list in the
@url{http://theworld.com/~cme/spki.txt, Simple Public-Key Infrastructure
(SPKI)}.
@end table

To export store files as an archive to the standard output, run:

M guix/pki.scm => guix/pki.scm +1 -0
@@ 24,6 24,7 @@
  #:use-module (rnrs io ports)
  #:export (%public-key-file
            %private-key-file
            %acl-file
            current-acl
            public-keys->acl
            acl->public-keys

M guix/scripts/archive.scm => guix/scripts/archive.scm +28 -0
@@ 32,6 32,7 @@
  #:use-module (srfi srfi-37)
  #:use-module (guix scripts build)
  #:use-module (guix scripts package)
  #:use-module (rnrs io ports)
  #:export (guix-archive))




@@ 111,6 112,9 @@ Export/import one or more packages from/to the store.\n"))
                    (lambda args
                      (leave (_ "invalid key generation parameters: ~s~%")
                             arg)))))
        (option '("authorize") #f #f
                (lambda (opt name arg result)
                  (alist-cons 'authorize #t result)))

        (option '(#\S "source") #f #f
                (lambda (opt name arg result)


@@ 256,6 260,28 @@ this may take time...~%"))
    ;; Make the public key readable by everyone.
    (chmod %public-key-file #o444)))

(define (authorize-key)
  "Authorize imports signed by the public key passed as an advanced sexp on
the input port."
  (define (read-key)
    (catch 'gcry-error
      (lambda ()
        (string->canonical-sexp (get-string-all (current-input-port))))
      (lambda (key err)
        (leave (_ "failed to read public key: ~a: ~a~%")
               (error-source err) (error-string err)))))

  (let ((key (read-key))
        (acl (current-acl)))
    (unless (eq? 'public-key (canonical-sexp-nth-data key 0))
      (leave (_ "s-expression does not denote a public key~%")))

    ;; Add KEY to the ACL and write that.
    (let ((acl (public-keys->acl (cons key (acl->public-keys acl)))))
      (with-atomic-file-output %acl-file
        (lambda (port)
          (display (canonical-sexp->string acl) port))))))

(define (guix-archive . args)
  (define (parse-options)
    ;; Return the alist of option values.


@@ 274,6 300,8 @@ this may take time...~%"))
        (cond ((assoc-ref opts 'generate-key)
               =>
               generate-key-pair)
              ((assoc-ref opts 'authorize)
               (authorize-key))
              (else
               (let ((store (open-connection)))
                 (cond ((assoc-ref opts 'export)

M tests/guix-archive.sh => tests/guix-archive.sh +3 -0
@@ 43,3 43,6 @@ guix archive --import < "$archive" 2>&1 | grep "import.*guile-bootstrap"

if guix archive something-that-does-not-exist
then false; else true; fi

if echo foo | guix archive --authorize
then false; else true; fi