/*
 * Copyright (c) 2002, The EROS Group, LLC and Johns Hopkins
 * University. All rights reserved.
 * 
 * This software was developed to support the EROS secure operating
 * system project (http://www.eros-os.org). The latest version of
 * the OpenCM software can be found at http://www.opencm.org.
 * 
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 * 
 * 3. Neither the name of the The EROS Group, LLC nor the name of
 *    Johns Hopkins University, nor the names of its contributors
 *    may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <opencm.h>

Mutable *
mutable_create(const char *name, const char *desc)
{
  Mutable *m = (Mutable *) GC_MALLOC(sizeof(Mutable));

  ser_init(m, &Mutable_SerType, Mutable_SerType.ver);
  SER_MODIFIED(m);

  m->uri = 0;

  m->seqNo = 0;
  m->nRevisions = 0;

  m->modified = TRUE;
  
  m->name = name;
  m->description = desc;
  m->readGroupURI = NULL;
  m->modGroupURI = NULL;
  m->notify = NULL;

  m->flags = 0u;

  return m;
}

/* Given a Mutable, determine the appropriate signature checking key
 * to use and verify that the mutable is authentically signed. if
 * things check out, return TRUE, else return FALSE. */
OC_bool
mutable_VerifySignature(Mutable *m)
{
  EVP_MD_CTX ctx;
  X509 *cert;
  EVP_PKEY *pubkey = NULL;
  const char *rememberSign = m->signature;
  const char *keyfile;
  SDR_stream *strm;
  URI *uri;
  int result = 0;
  uint32_t siglen;
  unsigned char *sigbuf = hex_decode(m->signature, &siglen);

  /* Need to extract origin from uri */
  uri = uri_create(m->uri);

  keyfile = path_join(opt_ConfigDir, CM_SERVER_SUBDIR);
  keyfile = path_join(keyfile, xstrcat(uri->netloc, ".pem"));

  if (!path_exists(keyfile))
    return FALSE;

  cert = ssl_read_cert_file(keyfile);
  pubkey = X509_get_pubkey(cert);

  m->signature = NULL;

  /* Serialize the Mutable (without the signature!) since that's what
   * the server originally signed.  Have the EVP library verify that
   * the serialized content was signed appropriately */
  strm = stream_createBuffer(SDR_RAW);
  m->signature = NULL;
  sdr_write("MUTABLE", strm, m); 
  stream_close(strm);

  EVP_VerifyInit(&ctx, EVP_sha1());

  {
    Buffer *buf = stream_asBuffer(strm);
    ocmoff_t end = buffer_length(buf);
    ocmoff_t pos = 0;

    while (pos < end) {
      BufferChunk bc = buffer_getChunk(buf, pos, end - pos);
      assert(bc.len <= (end - pos));

      EVP_VerifyUpdate(&ctx, bc.ptr, bc.len);
      
      pos += bc.len;
    }
  }

  result = EVP_VerifyFinal(&ctx, sigbuf, siglen, pubkey);

  /* Restore the Mutable's signature*/
  m->signature = rememberSign;

  return result == 1;
}

OC_bool
mutable_check(const void *ob)
{
  return TRUE;
}

void
mutable_show(const void *ob)
{
  const Mutable *m = ob;
  
  report(0, "URI:          %s\n", m->uri);
  report(0, "Sequence No:  %s\n", xunsigned64_str(m->seqNo));
  report(0, "nRevisions:   %s\n", xunsigned64_str(m->nRevisions));
  report(0, "Read Group:   %s\n", m->readGroupURI);
  report(0, "Mod Group:    %s\n", m->modGroupURI);
  report(0, "Notify:       %s\n", m->notify);
  report(0, "Name:         %s\n", m->name);
  report(0, "Description:  %s\n", m->description);
  report(0, "Flags:        0x%x\n", m->flags);
  report(0, "Signature     %s\n", m->signature);
}

void
mutable_serialize(SDR_stream *strm, const void *ob)
{
  const Mutable *m = ob;
  
  /* NOTE:  we don't serialize the truename or the
   * origin fields anymore!  Truename is computed
   * when needed and origin is specified within
   * the URI of the mutable. (Right now we set those
   * manually in deserialize().) */
  sdr_w_obname("uri", strm, m->uri);
  sdr_w_u64("seqNo", strm, m->seqNo);
  sdr_w_u64("nRevisions", strm, m->nRevisions);
  sdr_w_obname("readGroupTrueName", strm, m->readGroupURI);
  sdr_w_obname("modGroupTrueName", strm, m->modGroupURI);
  sdr_w_obname("notify", strm, m->notify);
  sdr_w_obname("name", strm, m->name);
  sdr_w_string("desc", strm, m->description);
  sdr_w_u32("flags", strm, m->flags);
  sdr_w_obname("signature", strm, m->signature);
}

void *
mutable_deserialize(const DeserializeInfo *di, SDR_stream *strm)
{
  URI *uri;
  const char *temp;
  Mutable *m = (Mutable *) GC_MALLOC(sizeof(Mutable));

  ser_init(m, &Mutable_SerType, di->ver);

  SER_MODIFIED(m);

  m->uri               = sdr_r_obname("uri", strm);
  m->seqNo             = sdr_r_u64("seqNo", strm);
  m->nRevisions        = sdr_r_u64("nRevisions", strm);
  m->readGroupURI      = sdr_r_obname("readGroupTrueName", strm);
  m->modGroupURI       = sdr_r_obname("modGroupTrueName", strm);
  m->notify            = sdr_r_obname("notify", strm);
  m->name              = sdr_r_obname("name", strm);
  m->description       = sdr_r_string("desc", strm);
  m->flags             = sdr_r_u32("flags", strm);
  m->signature         = sdr_r_obname("signature", strm);

  /* Now make the origin and serTrueName fields manually: */
  uri = uri_create(m->uri);
  temp = xstrcat("_", uri->path + 1);
  temp = xstrcat(uri->netloc, temp);

  return m;
}

void
mutable_mark(Repository *r, const void *container,
	     const void *ob, rbtree *memObs)
{
  const Mutable *m = ob;
  
  /* NOTE:  we don't serialize the truename or the
   * origin fields anymore!  Truename is computed
   * when needed and origin is specified within
   * the URI of the mutable. (Right now we set those
   * manually in deserialize().) */

  assert(container == ob);

  mark_addmark(container, memObs, m->readGroupURI);
  mark_addmark(container, memObs, m->modGroupURI);
  mark_addmark(container, memObs, m->notify);
}
