#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <libxml/xmlwriter.h>
#include <libxml/xmlreader.h>
#include <inttypes.h>
#include <unistd.h>
#include "xar.h"
#include "archive.h"
#include "b64.h"
#include "signature.h"
struct _signature_copy_context{
void *buffer;
size_t length;
off_t offset;
};
#ifdef __APPLE__
xar_signature_t xar_signature_new_internal(xar_t x, int is_extended, const char *type, int32_t length, xar_signer_callback callback, void *callback_context)
#else
xar_signature_t xar_signature_new(xar_t x,const char *type, int32_t length, xar_signer_callback callback, void *callback_context)
#endif
{
xar_signature_t ret;
if( XAR(x)->files ){
xar_err_new(x);
xar_err_set_string(x, "Signatures must be added before files are added");
xar_err_callback(x, XAR_SEVERITY_WARNING, XAR_ERR_ARCHIVE_CREATION);
return NULL;
}
ret = malloc(sizeof(struct __xar_signature_t));
if( ! ret )
return NULL;
memset(XAR_SIGNATURE(ret), 0, sizeof(struct __xar_signature_t));
XAR_SIGNATURE(ret)->type = strdup(type);
XAR_SIGNATURE(ret)->len = length;
XAR_SIGNATURE(ret)->offset = XAR(x)->heap_offset;
XAR_SIGNATURE(ret)->signer_callback = callback;
XAR_SIGNATURE(ret)->callback_context = callback_context;
#ifdef __APPLE__
XAR_SIGNATURE(ret)->is_extended = is_extended;
#endif
XAR(x)->heap_offset += length;
XAR(x)->heap_len += length;
if( XAR_SIGNATURE(XAR(x)->signatures) ) {
struct __xar_signature_t *sig;
for( sig = XAR_SIGNATURE(XAR(x)->signatures); sig->next; sig = sig->next);
sig->next = XAR_SIGNATURE(ret);
} else
XAR(x)->signatures = ret;
XAR_SIGNATURE(ret)->x = x;
return ret;
}
#ifdef __APPLE__
xar_signature_t xar_signature_new(xar_t x,const char *type, int32_t length, xar_signer_callback callback, void *callback_context)
{
return xar_signature_new_internal(x, 0, type, length, callback, callback_context);
}
#endif
xar_signature_t xar_signature_new_extended(xar_t x,const char *type, int32_t length, xar_signer_callback callback, void *callback_context)
{
#ifdef __APPLE__
return xar_signature_new_internal(x, 1, type, length, callback, callback_context);
#else
return xar_signature_new(x, type, length, callback, callback_context);
#endif
}
const char *xar_signature_type(xar_signature_t s)
{
if( !s )
return NULL;
return XAR_SIGNATURE(s)->type;
}
int32_t xar_signature_add_x509certificate(xar_signature_t sig, const uint8_t *cert_data, uint32_t cert_len )
{
struct __xar_x509cert_t *newcert;
if( !sig )
return -1;
newcert = malloc(sizeof(struct __xar_x509cert_t));
memset(newcert, 0, sizeof(struct __xar_x509cert_t));
newcert->content = malloc(sizeof(const char)*cert_len);
memcpy(newcert->content,cert_data,cert_len);
newcert->len = cert_len;
if( XAR_SIGNATURE(sig)->x509certs ){
struct __xar_x509cert_t *cert;
for( cert = XAR_SIGNATURE(sig)->x509certs; cert->next; cert = cert->next );
cert->next = newcert;
}else{
XAR_SIGNATURE(sig)->x509certs = newcert;
}
XAR_SIGNATURE(sig)->x509cert_count++;
return 0;
}
int32_t xar_signature_get_x509certificate_count(xar_signature_t sig)
{
if( !sig ){
return 0;
}
return XAR_SIGNATURE(sig)->x509cert_count;
}
int32_t xar_signature_get_x509certificate_data(xar_signature_t sig, int32_t index, const uint8_t **cert_data, uint32_t *cert_len)
{
struct __xar_x509cert_t *cert;
int i = 0;
if( !XAR_SIGNATURE(sig)->x509cert_count ){
if( cert_data )
*cert_data = 0;
return -1;
}
for( cert = XAR_SIGNATURE(sig)->x509certs; cert; cert = cert->next ){
if( i == index ){
if( cert_data )
*cert_data = cert->content;
if( cert_len )
*cert_len = cert->len;
break;
}
i++;
}
if( !cert ){
return -1;
}
return 0;
}
xar_signature_t xar_signature_first(xar_t x)
{
return XAR(x)->signatures;
}
xar_signature_t xar_signature_next(xar_signature_t s)
{
return XAR_SIGNATURE(s)->next;
}
int32_t _xar_signature_read_from_heap(xar_t x ,off_t offset,size_t length,uint8_t *data)
{
off_t seek_off = XAR(x)->toc_count + sizeof(xar_header_t) + offset;
int r = 0;
r = lseek(XAR(x)->fd, seek_off, SEEK_SET);
if( -1 == r ){
xar_err_new(x);
xar_err_set_string(x, "Unable to seek");
xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
return -1;
}
r = read(XAR(x)->fd,data,length);
if( r != length ){
xar_err_new(x);
xar_err_set_string(x, "Unable to read");
xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
return -1;
}
return 0;
}
uint8_t xar_signature_copy_signed_data(xar_signature_t sig, uint8_t **data, uint32_t *length, uint8_t **signed_data, uint32_t *signed_length, off_t *signed_offset)
{
uint32_t offset = 0;
xar_t x = NULL;
const char *value;
if( !sig )
return -1;
x = XAR_SIGNATURE(sig)->x;
if(length) {
if(0 == xar_prop_get( XAR_FILE(x) , "checksum/size", &value)){
*length = strtoull( value, (char **)NULL, 10);
}
if(0 == xar_prop_get( XAR_FILE(x) , "checksum/offset", &value)){
offset = strtoull( value, (char **)NULL, 10);
}
if(data) {
*data = malloc(sizeof(char)*(*length));
_xar_signature_read_from_heap(x, offset, *length, *data);
}
}
offset = XAR_SIGNATURE(sig)->offset;
if( signed_offset )
*signed_offset = offset;
if(signed_length) {
*signed_length = XAR_SIGNATURE(sig)->len;
if(signed_data) {
*signed_data = malloc(sizeof(char)*(*signed_length));
_xar_signature_read_from_heap(x, offset, *signed_length, *signed_data);
}
}
return 0;
}
xar_signature_t xar_signature_unserialize(xar_t x, xmlTextReaderPtr reader)
{
struct __xar_signature_t *ret = NULL;
#ifdef __APPLE__
const xmlChar *sig_type = NULL;
#endif
const xmlChar *value = NULL;
const xmlChar *name = NULL;
int type;
size_t outputLength = 0;
ret = malloc(sizeof(struct __xar_signature_t));
if( ! ret )
return NULL;
memset(ret, 0, sizeof(struct __xar_signature_t));
ret->x = x;
#ifdef __APPLE__
sig_type = xmlTextReaderConstLocalName(reader);
if(strcmp((const char*)sig_type, "x-signature") == 0) {
ret->is_extended = 1;
}
#endif
value = xmlTextReaderGetAttribute(reader, (const xmlChar*)"style");
if( value ){
ret->type = strdup((const char *)value);
}
while( xmlTextReaderRead(reader) == 1 ) {
type = xmlTextReaderNodeType(reader);
name = xmlTextReaderConstLocalName(reader);
if (value) {
xmlFree((void*)value);
value = NULL;
}
if( type == XML_READER_TYPE_ELEMENT ) {
if(strcmp((const char*)name, "size") == 0) {
while( xmlTextReaderRead(reader) == 1 ) {
type = xmlTextReaderNodeType(reader);
if( type == XML_READER_TYPE_TEXT ) {
value = xmlTextReaderConstValue(reader);
ret->len = strtoull( (const char *)value, (char **)NULL, 10);
value = NULL;
}else if( type == XML_READER_TYPE_END_ELEMENT ) {
break;
}
}
} else if( strcmp((const char*)name, "offset") == 0 ){
while( xmlTextReaderRead(reader) == 1 ) {
type = xmlTextReaderNodeType(reader);
if( type == XML_READER_TYPE_TEXT ) {
value = xmlTextReaderConstValue(reader);
ret->offset = strtoull( (const char *)value, (char **)NULL, 10);
value = NULL;
}else if( type == XML_READER_TYPE_END_ELEMENT ) {
break;
}
}
} else if( strcmp((const char*)name, "KeyInfo") == 0 ){
while( xmlTextReaderRead(reader) == 1 ) {
type = xmlTextReaderNodeType(reader);
name = xmlTextReaderConstLocalName(reader);
if( type == XML_READER_TYPE_ELEMENT ) {
if(strcmp((const char*)name, "X509Data") == 0) {
while( xmlTextReaderRead(reader) == 1 ) {
type = xmlTextReaderNodeType(reader);
name = xmlTextReaderConstLocalName(reader);
if( type == XML_READER_TYPE_ELEMENT ) {
if(strcmp((const char*)name, "X509Certificate") == 0) {
while( xmlTextReaderRead(reader) == 1 ) {
type = xmlTextReaderNodeType(reader);
if( type == XML_READER_TYPE_TEXT ) {
unsigned char *sig_data = NULL;
value = xmlTextReaderConstValue(reader);
sig_data = xar_from_base64(value, strlen((const char *)value), &outputLength);
xar_signature_add_x509certificate(ret, sig_data, outputLength);
free(sig_data);
value = NULL;
}else if( type == XML_READER_TYPE_END_ELEMENT ) {
break;
}
}
}
}else if( type == XML_READER_TYPE_END_ELEMENT ) {
break;
}
}
}
}else if( type == XML_READER_TYPE_END_ELEMENT ) {
break;
}
}
}
}else if( type == XML_READER_TYPE_TEXT ) {
value = xmlTextReaderConstValue(reader);
value = NULL;
break;
}else if( type == XML_READER_TYPE_END_ELEMENT ) {
break;
}
}
if (value) {
xmlFree((void*)value);
value = NULL;
}
return ret;
}
int32_t xar_signature_serialize(xar_signature_t sig, xmlTextWriterPtr writer)
{
if( !sig ) return 0;
#ifdef __APPLE__
const char* element_name = XAR_SIGNATURE(sig)->is_extended ? "x-signature" : "signature";
xmlTextWriterStartElementNS( writer, NULL, BAD_CAST(element_name), NULL);
#else
xmlTextWriterStartElementNS( writer, NULL, BAD_CAST("signature"), NULL);
#endif
xmlTextWriterWriteAttribute(writer, BAD_CAST("style"), BAD_CAST(XAR_SIGNATURE(sig)->type));
xmlTextWriterStartElementNS( writer, NULL, BAD_CAST("offset"), NULL);
xmlTextWriterWriteFormatString(writer, "%"PRIu64, (uint64_t)(XAR_SIGNATURE(sig)->offset));
xmlTextWriterEndElement(writer);
xmlTextWriterStartElementNS( writer, NULL, BAD_CAST("size"), NULL);
xmlTextWriterWriteFormatString(writer, "%d", (XAR_SIGNATURE(sig)->len));
xmlTextWriterEndElement(writer);
xmlTextWriterStartElementNS( writer, NULL, BAD_CAST("KeyInfo"), NULL);
xmlTextWriterWriteAttribute(writer, BAD_CAST("xmlns"), BAD_CAST("http://www.w3.org/2000/09/xmldsig#"));
if( XAR_SIGNATURE(sig)->x509certs ){
struct __xar_x509cert_t *cert;
xmlTextWriterStartElementNS( writer, NULL, BAD_CAST("X509Data"), NULL);
for( cert = XAR_SIGNATURE(sig)->x509certs; cert; cert = cert->next ){
xmlTextWriterStartElementNS( writer, NULL, BAD_CAST("X509Certificate"), NULL);
xmlTextWriterWriteBase64( writer, (const char *)cert->content, 0, cert->len);
xmlTextWriterEndElement(writer);
}
xmlTextWriterEndElement(writer);
}
xmlTextWriterEndElement(writer);
xmlTextWriterEndElement(writer);
if( XAR_SIGNATURE(sig)->next )
xar_signature_serialize(XAR_SIGNATURE(sig)->next,writer);
return 0;
}
void _xar_signature_remove_cert(struct __xar_x509cert_t *cert)
{
struct __xar_x509cert_t *next;
if( !cert )
return;
next = cert->next;
if( cert->content )
free(cert->content);
free(cert);
_xar_signature_remove_cert(next);
return;
}
void xar_signature_remove(xar_signature_t sig)
{
xar_signature_t next;
if( !sig )
return;
next = XAR_SIGNATURE(sig)->next;
if( XAR_SIGNATURE(sig)->type )
free(XAR_SIGNATURE(sig)->type);
if( XAR_SIGNATURE(sig)->x509cert_count ){
_xar_signature_remove_cert(XAR_SIGNATURE(sig)->x509certs);
}
free(XAR_SIGNATURE(sig));
xar_signature_remove(next);
return;
}