Skip to content

Commit

Permalink
validate content on key uploads
Browse files Browse the repository at this point in the history
Signed-off-by: Dave Parfitt <[email protected]>

Pull request: #704
Approved by: reset
  • Loading branch information
Dave Parfitt authored and thesentinels committed Jun 10, 2016
1 parent 2489fcb commit c3eb19d
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 34 deletions.
128 changes: 95 additions & 33 deletions components/core/src/crypto/keys/sig_key_pair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,39 +225,10 @@ impl SigKeyPair {
pub fn write_file_from_str<P: AsRef<Path> + ?Sized>(content: &str,
cache_key_path: &P)
-> Result<(Self, PairType)> {
let mut lines = content.lines();
let pair_type = match lines.next() {
Some(val) => {
match val {
PUBLIC_SIG_KEY_VERSION => PairType::Public,
SECRET_SIG_KEY_VERSION => PairType::Secret,
_ => {
return Err(Error::CryptoError(format!("Unsupported key version: {}", val)))
}
}
}
None => {
let msg = format!("write_sig_key_from_str:1 Malformed sig key string:\n({})",
content);
return Err(Error::CryptoError(msg));
}
};
let name_with_rev = match lines.next() {
Some(val) => val,
None => {
let msg = format!("write_sig_key_from_str:2 Malformed sig key string:\n({})",
content);
return Err(Error::CryptoError(msg));
}
};
let key_body = match lines.nth(1) {
Some(val) => val,
None => {
let msg = format!("write_sig_key_from_str:3 Malformed sig key string:\n({})",
content);
return Err(Error::CryptoError(msg));
}
};
let (pair_type, name_with_rev, key_body) = try!(Self::parse_key_str(content));
let name_with_rev = &name_with_rev;
let key_body = &key_body;

let suffix = match pair_type {
PairType::Public => PUBLIC_KEY_SUFFIX,
PairType::Secret => SECRET_SIG_KEY_SUFFIX,
Expand Down Expand Up @@ -320,6 +291,97 @@ impl SigKeyPair {
Ok((try!(Self::get_pair_for(&name_with_rev, cache_key_path)), pair_type))
}

/// Parses a string slice of a public or secret signature key.
///
/// The return valid is a tuple consisting of:
/// `(PairType, name_with_rev::String, key_body::String)`
///
/// # Examples
///
/// With a public key:
///
/// ```
/// extern crate habitat_core;
///
/// use habitat_core::crypto::SigKeyPair;
/// use habitat_core::crypto::keys::PairType;
///
/// fn main() {
/// let content = "SIG-PUB-1
/// unicorn-20160517220007
///
/// J+FGYVKgragA+dzQHCGORd2oLwCc2EvAnT9roz9BJh0=";
/// let (pair_type, name_with_rev, key_body) = SigKeyPair::parse_key_str(content).unwrap();
/// assert_eq!(pair_type, PairType::Public);
/// assert_eq!(name_with_rev, "unicorn-20160517220007");
/// assert_eq!(key_body, "J+FGYVKgragA+dzQHCGORd2oLwCc2EvAnT9roz9BJh0=");
/// }
/// ```
///
/// With a secret key:
///
/// ```
/// extern crate habitat_core;
///
/// use habitat_core::crypto::SigKeyPair;
/// use habitat_core::crypto::keys::PairType;
///
/// fn main() {
/// let content = "SIG-SEC-1
/// unicorn-20160517220007
///
/// jjQaaphB5+CHw7QzDWqMMuwhWmrrHH+SzQAgRrHfQ8sn4UZhUqCtqAD53NAcIY5F3agvAJzYS8CdP2ujP0EmHQ==";
///
/// let (pair_type, name_with_rev, key_body) = SigKeyPair::parse_key_str(content).unwrap();
/// assert_eq!(pair_type, PairType::Secret);
/// assert_eq!(name_with_rev, "unicorn-20160517220007");
/// assert_eq!(key_body, "jjQaaphB5+CHw7QzDWqMMuwhWmrrHH+SzQAgRrHfQ8sn4UZhUqCtqAD53NAcIY5F3agvAJzYS8CdP2ujP0EmHQ==");
/// }
/// ```
///
/// # Errors
///
/// * If there is a key version mismatch
/// * If the key version is missing
/// * If the key name with revision is missing
/// * If the key value (the Bas64 payload) is missing
pub fn parse_key_str(content: &str) -> Result<(PairType, String, String)> {
let mut lines = content.lines();
let pair_type = match lines.next() {
Some(val) => {
match val {
PUBLIC_SIG_KEY_VERSION => PairType::Public,
SECRET_SIG_KEY_VERSION => PairType::Secret,
_ => {
return Err(Error::CryptoError(format!("Unsupported key version: {}", val)))
}
}
}
None => {
let msg = format!("write_sig_key_from_str:1 Malformed sig key string:\n({})",
content);
return Err(Error::CryptoError(msg));
}
};
let name_with_rev = match lines.next() {
Some(val) => val,
None => {
let msg = format!("write_sig_key_from_str:2 Malformed sig key string:\n({})",
content);
return Err(Error::CryptoError(msg));
}
};
let key_body = match lines.nth(1) {
Some(val) => val,
None => {
let msg = format!("write_sig_key_from_str:3 Malformed sig key string:\n({})",
content);
return Err(Error::CryptoError(msg));
}
};
Ok((pair_type, name_with_rev.to_string(), key_body.to_string()))
}

fn get_public_key(key_with_rev: &str, cache_key_path: &Path) -> Result<SigPublicKey> {
let public_keyfile = mk_key_filename(cache_key_path, key_with_rev, PUBLIC_KEY_SUFFIX);
let bytes = try!(read_key_bytes(&public_keyfile));
Expand Down
46 changes: 45 additions & 1 deletion components/depot/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ use std::sync::{Arc, Mutex};
use bodyparser;
use dbcache::{self, BasicSet, IndexSet};
use hab_core::package::{Identifiable, FromArchive, PackageArchive};
use hab_core::crypto::keys;
use hab_core::crypto::keys::{self, PairType};
use hab_core::crypto::SigKeyPair;
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
use iron::headers::ContentType;
use iron::prelude::*;
Expand Down Expand Up @@ -497,6 +498,26 @@ fn upload_origin_key(depot: &Depot, req: &mut Request) -> IronResult<Response> {
return Ok(Response::with(status::Forbidden));
}

let mut content = String::new();
if let Err(e) = req.body.read_to_string(&mut content) {
debug!("Can't read public key upload content: {}", e);
return Ok(Response::with(status::BadRequest));
}

match SigKeyPair::parse_key_str(&content) {
Ok((PairType::Public, _, _)) => {
debug!("Received a valid public key");
}
Ok(_) => {
debug!("Received a secret key instead of a public key");
return Ok(Response::with(status::BadRequest));
}
Err(e) => {
debug!("Invalid public key content: {}", e);
return Ok(Response::with(status::BadRequest));
}
}

let origin_keyfile = depot.key_path(&origin, &revision);
debug!("Writing key file {}", origin_keyfile.to_string_lossy());
if origin_keyfile.is_file() {
Expand Down Expand Up @@ -561,6 +582,29 @@ fn upload_origin_secret_key(depot: &Depot, req: &mut Request) -> IronResult<Resp
debug!("Can't read key content {}", e);
return Ok(Response::with(status::BadRequest));
}

match String::from_utf8(key_content.clone()) {
Ok(content) => {
match SigKeyPair::parse_key_str(&content) {
Ok((PairType::Secret, _, _)) => {
debug!("Received a valid secret key");
}
Ok(_) => {
debug!("Received a public key instead of a secret key");
return Ok(Response::with(status::BadRequest));
}
Err(e) => {
debug!("Invalid secret key content: {}", e);
return Ok(Response::with(status::BadRequest));
}
}
}
Err(e) => {
debug!("Can't parse secret key upload content: {}", e);
return Ok(Response::with(status::BadRequest));
}
}

request.set_body(key_content);
request.set_owner_id(0);

Expand Down

0 comments on commit c3eb19d

Please sign in to comment.