OpenPGP Signed Commits
Abstract
CVS does not provide verification of past revisions of files. Attackers with access to a CVS repository could replace file contents or add new revisions apparently from a project member without users noticing on checkout.
This document proposes changes to CVS which would allow committers to sign revisions using GPG at commit time and which would allow verification of data transmitted by the server on the client end.
Goals
- Sign commits.
- Maximize data verifiable by the client.
- Minimize client-server transfer.
Potential Problems
RCS Keywords
RCS keyword substitution as it is currently implemented is incompatible with naive commit signatures. Since the CVS server currently substitutes meta-data about the revision being checked out at the time of checkout or update, the signature for any commit would be instantly invalidated at checkout time for a file with keywords, by virtue of the original signed file contents and the new file being different.
Naively verifying the original file and allowing the client to perform the keyword substitution using unsigned server data would be insecure, allowing this GPG-Signed Commits RCS Keyword Exploit.
Allowing the client to sign the commit meta-data at commit time is not feasible. Some of the data could be signed given an extra network-turnaround, since data such as the commit time and the new revision number must be generated on the server end. Unfortunately some potential keyword substitution data can't be known until checkout time. For example, the $Name$ keyword is replaced with the name of the tag or branch used to check out the revision and the tag or branch need not to have even existed at the time of the commit.
There are several directions to take this, including forcing users to sign their tag and branch names at creation time, or providing some simple pattern matching on the client end to minimize the potential for exploit, but for purposes of simplifying the signing problem in this document, RCS keywords will be completely ignored.
Required Changes
Handle RCS "openpgp-signatures" newphrase in archives.
Archive parser (rcs.c) needs to handle the newphrase and store the signatures somewhere in the revision structure. The new openpgp-signatures phrase would be part of the delta node in the RCS archive, like so:
delta ::= num
date num;
author id;
state {id};
branches {num}*;
next {num}*;
openpgp-signatures {string}*;
{ newphrase }*
Please see rcsfile(5) for the full RCS archive file syntax.
The RCS code can treat the signatures as an opaque string.
Always use CVS/Base files
Several CVS commands will now need to reference the signatures for files on the client side. The simplest way to do this and be able to apply diffs and patches to them as necessary, would seem to be to store the base revisions in CVS/Base, as is currently being done for cvs edit, as well as the signature files.
Update Sandbox without Base Files
No special sandbox upgrade path is necessary. The server will attempt to send the patch against the Base revision and the client will reject it and request the full file. After that, the client will have the Base revision.
New Base naming convention
Since several versions of the same file may need to be stored, the current naming convention for CVS/Base files will need to be changed. I suggest adopting the convention currently used for backup files since it seems unlikely to impinge upon existing user namespaces: .#filename.revision.
Signatures in Base
Signatures for revisions of files currently stored in Base can also be stored in base. The naming convention would be GPG's: filename.sig (or in this case .#filename.revision.sig) would also avoid impinging on any other existing namespaces. Multiple signatures may simply be concatenated into the same file.
Line Ending Conventions
Since CVS converts line endings (EOLs) and this would invalidate signatures, Base files should be stored in the canonical, repository EOL format. Clients will need to perform EOL conversion on Base files whenever they are converted to a form to be presented to the user.
This has the secondary and potentially useful side-effect of allowing users with a little knowledge of CVS's sandbox metadata structure to verify Base revisions directly, using GPG, without the need for the CVS client.
Beneficial Side-effects
Send Client-side diffs Against Base Files
Since the client can now usually be depended on to have a Base file for whatever revision it currently has checked out, the client can send diffs against the Base file rather than full file contents to the server when necessary.
cvs diff against Base Revisions can Disconnect
cvs diff against Base revisions, a common request, will no longer need to contact the server. The new cvs verify command would also be able to verify base revisions without contacting the server.
Commit
Client Signs Revision
- Scan for keywords in file. Provide an error or a warning when found? Think this should be an error, with some syntax available to force the commit anyhow.
- For text files, convert to canonical, repository line-ending format before signing.
- Requires GPG Template specification. May have a default, like
/usr/bin/gpg --detach-sign %f, where%fis the name of the file to sign, but will need to be user-specifiable. Specifiying this in a command-line arg is probably best. Users who want permanance may add it to their .cvsrc. - If user wants to specify an alternate key, it should be in the template.
- After signing, client sends detached signature in a new server request.
Server Receives Signature
- Recieves new server request.
- Verify signature, as per the verify command? This should probably be configurable per-server. This is necessary to avoid allowing unusable signatures to be inserted by, for instance, a broken user --gpg-sign-template on a client, but would unfortunately mean that the server needs all committers' keys installed in its keyring to allow commits. For users with different passwords for their SSH & GPG keys, this could be seen as a second layer of authentication when enabled. Problems of not verifying on the server end may be mitigated by allowing the New Sign Command to delete signatures.
- Save new signatures in RCS "gpg-signatures" newphrase.
Checkout/Update
Vanilla
Just verify the checked-out file, like the New Verify Command.
In Conjunction with a Merge
Need to perform diff/merge on the client and verify signatures, like the new verify command, for each revision involved. Intermediate revisions may all be stored in CVS/Base and server may send diffs against the Base revision the client is already known to have to save bandwidth.
Despite the brief summary here, this may be the most complicated feature to add, next to the new CVS/Base handling itself, since it means that diffs and merges that used to be performed on the server with results returned in a single operation will now need to be replaced with the sending of up to four files to the client, and the client will then need to verify each of these files and perform the merges itself.
cvs log
Should display the signatures as part of the revision information, probably as summary, unless some command-line option suppresses it. For revisions that are present locally, log could potentially automatically verify. For instance:
... ---------------------------- revision 1.1251 date: 2005/09/06 21:56:30; author: selse; state: Exp; lines: +3 -0 Good untrusted signature from So And So <so.and.so@wherever.com> Good trusted signature from Somebody Else <selse@somewhere.org> Bad signature from Who Ever <who@knows.edu> * thisfile (thatfunction): Added some lines. ---------------------------- revision 1.1250 date: 2005/09/05 18:34:37; author: selse; state: Exp; lines: +0 -5 Signature from So And So <so.and.so@wherever.com> Signature from Somebody Else <selse@somewhere.org> * thatfile (foo): Delete obsolete function. ---------------------------- ...
It was originally suggested that cvs log print the whole signature, but this is probably messy and unnecessary since the signatures will be stored in CVS/Base.
New Verify Command
Verifies the Base Revision.
--gpg-verify-template
Like the --gpg-sign-template, this requires a command-line template for verifying, perhaps defaulting to:
/usr/bin/gpg --verify %s %f
Where %s is the name of the file containing the signature and %f is the name of the signed file.
Algorithm
- If the revision to be verified, or its signature file, does not exist in CVS/Base, fetch them.
- Verify the revisions.
Other Notes
cvs verify -r -p or some such should cat the signature to stdout rather than actually verifying.
cvs status should automatically verify when the CVS/Base file is found.
New Sign Command
Allows new signatures to be attached to old revisions. This might be useful for re-signing a revision after a key is revoked, at the least. Should probably also have a -d option available only to the cvsadmin group, which allows old signatures to be deleted.
Remaining Vulnerabilities
Please see Vulnerabilities Related to GPG-signing.
References
The original info-cvs thread suggesting this idea.
The Savannah tracker for this issue.
RFC 2440 (OpenPGP RFC).
In other SCMs
Software Configuration Management (SCM) Security (essay)
Annoted version of the above wrt Aegis
Security aspects of Bazaar-NG (old)
Security aspects of Bazaar-NG (new)
In practice, how it works for GNU 'tla' Arch (note that each archive has its own GPG commands)
![[ Valid XHTML 1.0! ]](/branding/w3c-valid-xhtml10-44x16.png)
![[ Valid CSS! ]](/branding/w3c-valid-css-44x16.png)
