Added CREDENTIAL frame support.

This commit is contained in:
Tatsuhiro Tsujikawa 2012-04-06 01:45:39 +09:00
parent 93953c102b
commit d83d1cd33a
19 changed files with 1531 additions and 54 deletions

View File

@ -34,13 +34,15 @@ lib_LTLIBRARIES = libspdylay.la
OBJECTS = spdylay_pq.c spdylay_map.c spdylay_queue.c \
spdylay_buffer.c spdylay_frame.c spdylay_zlib.c \
spdylay_session.c spdylay_helper.c spdylay_stream.c spdylay_npn.c \
spdylay_submit.c spdylay_outbound_item.c
spdylay_submit.c spdylay_outbound_item.c \
spdylay_client_cert_vector.c
HFILES = spdylay_pq.h spdylay_int.h spdylay_map.h spdylay_queue.h \
spdylay_buffer.h spdylay_frame.h spdylay_zlib.h \
spdylay_session.h spdylay_helper.h spdylay_stream.h spdylay_int.h \
spdylay_npn.h \
spdylay_submit.h spdylay_outbound_item.h \
spdylay_client_cert_vector.h \
spdylay_net.h
libspdylay_la_SOURCES = $(HFILES) $(OBJECTS)

View File

@ -154,6 +154,11 @@ typedef enum {
* multiple in-sequence NUL bytes).
*/
SPDYLAY_ERR_INVALID_HEADER_BLOCK = -518,
/**
* Indicates that the context is not suitable to perform the
* requested operation.
*/
SPDYLAY_ERR_INVALID_STATE = -519,
/**
* The errors < :enum:`SPDYLAY_ERR_FATAL` mean that the library is
* under unexpected condition and cannot process any further data
@ -214,7 +219,11 @@ typedef enum {
/**
* The WINDOW_UPDATE control frame. This first appeared in SPDY/3.
*/
SPDYLAY_WINDOW_UPDATE = 9
SPDYLAY_WINDOW_UPDATE = 9,
/**
* The CREDENTIAL control frame. This first appeared in SPDY/3.
*/
SPDYLAY_CREDENTIAL = 10
} spdylay_frame_type;
/**
@ -635,6 +644,52 @@ typedef struct {
int32_t delta_window_size;
} spdylay_window_update;
/**
* @struct
*
* The structure to hold chunk of memory.
*/
typedef struct {
/**
* The pointer to the data.
*/
uint8_t *data;
/**
* The length of the data.
*/
size_t length;
} spdylay_mem_chunk;
/**
* @struct
*
* The CREDENTIAL control frame. This first appeared in SPDY/3. It has
* the following members:
*/
typedef struct {
/**
* The control frame header.
*/
spdylay_ctrl_hd hd;
/**
* The index in the client certificate vector.
*/
uint16_t slot;
/**
* Cryptographic proof that the client has possession of the private
* key associated with the certificate.
*/
spdylay_mem_chunk proof;
/**
* The certificate chain. The certs[0] is the leaf certificate.
*/
spdylay_mem_chunk *certs;
/**
* The number of certificates in |certs|.
*/
size_t ncerts;
} spdylay_credential;
/**
* @struct
*
@ -747,6 +802,10 @@ typedef union {
* The WINDOW_UPDATE control frame.
*/
spdylay_window_update window_update;
/**
* The CREDENTIAL control frame.
*/
spdylay_credential credential;
} spdylay_frame;
/**
@ -895,54 +954,88 @@ typedef void (*spdylay_on_stream_close_callback)
typedef void (*spdylay_on_request_recv_callback)
(spdylay_session *session, int32_t stream_id, void *user_data);
#define SPDYLAY_MAX_SCHEME 255
#define SPDYLAY_MAX_HOSTNAME 255
struct spdylay_origin;
/**
* @struct
*
* The Web origin structure. The origin is the tuple (scheme, host,
* port). The details of this structure is intentionally hidden. To
* access these members, use accessor functions below.
*/
typedef struct spdylay_origin spdylay_origin;
/**
* @function
*
* Returns the scheme member of the |origin|.
*/
const char* spdylay_origin_get_scheme(const spdylay_origin *origin);
/**
* @function
*
* Returns the host member of the |origin|.
*/
const char* spdylay_origin_get_host(const spdylay_origin *origin);
/**
* @function
*
* Returns the port member of the |origin|.
*/
uint16_t spdylay_origin_get_port(const spdylay_origin *origin);
/**
* @functypedef
*
* Callback function invoked when the library wants to know whether
* the client certificate is required for the given |origin| and if so
* requests the cryptographic proof for the certificate. The |origin|
* is the hostname and port number joined with ':' (e.g.,
* example.org:8443). The implementation of this function must assign
* the pointer to the buffer where proof is stored to the |*proof_ptr|
* and its length to the |*prooflen_ptr|. Return 0 if the function
* succeeds. If no client certificate is required for the |origin|,
* the function must return SPDYLAY_ERR_CLIENT_CERT_NOT_NEEDED.
* (TODO: add error code)
* Callback function invoked when the library needs the cryptographic
* proof that the client has possession of the private key associated
* with the certificate for the given |origin|. If called with
* |prooflen| == 0, the implementation of this function must return
* the length of the proof in bytes. If called with |prooflen| > 0,
* write proof into |proof| exactly |prooflen| bytes and return 0.
*
* The data stored in |*proof_ptr| will be copied just after the
* function call. This copy lives until the CREDENTIAL frame is
* sent. Because the client certificate vector has limited number of
* slots, the application code may be required to pass the same proof
* more than once.
* Because the client certificate vector has limited number of slots,
* the application code may be required to pass the same proof more
* than once.
*/
typedef int (*spdylay_get_credential_proof)
(spdylay_session *session, const char *origin,
uint8_t **proof_ptr, size_t *prooflen_ptr, void *user_data);
typedef ssize_t (*spdylay_get_credential_proof)
(spdylay_session *session, const spdylay_origin *origin,
uint8_t *proof, size_t prooflen, void *user_data);
/**
* @functypedef
*
* Callback function invoked when the library needs the length of the
* client certificate chain for the given |origin|. The
* implementation of this function must return the length of the
* client certificate chain. If no client certificate is required for
* the given |origin|, return 0. If positive integer is returned,
* :type:`spdylay_get_credential_proof` and
* :type:`spdylay_get_credential_cert` callback functions will be used
* to get the cryptographic proof and certificate respectively.
*/
typedef ssize_t (*spdylay_get_credential_ncerts)
(spdylay_session *session, const spdylay_origin *origin, void *user_data);
/**
* @functypedef
*
* Callback function invoked when the library needs the client
* certificate for the given |origin|. The |origin| is the hostname
* and port number joined with ':' (e.g., example.org:8443). The
* implementation of this function must assign the pointer to the
* buffer where certificate is stored to the |*cert_ptr| and its
* length to the |*certlen_ptr|. Because the library requires the
* certificate chain, this callback function will be called repeatedly
* to get certificate chain starting with the leaf certificate.
* Assign 0 to |*certlen_ptr| if there is no more
* certificate. Currently, the library does not expect for this
* function to fail. Therefore the function must return 0.
*
* The data stored in |*cert_ptr| will be copied just after the
* function call. This copy lives until the CREDENTIAL frame is
* sent. Because the client certificate vector has limited number of
* slots, the application code may be required to pass the same
* certificate more than once.
* certificate for the given |origin|. The |index| is the index of the
* certificate chain and 0 means the leaf certificate of the chain.
* If called with |certlen| == 0, the implementation of this function
* must return the length of the certificate in bytes. If called with
* |certlen| > 0, write certificate into |cert| exactly |certlen|
* bytes and return 0.
*/
typedef int (*spdylay_get_credential_cert)
(spdylay_session *session, const char* origin,
uint8_t **cert_ptr, size_t *certlen_ptr, void *user_data);
typedef ssize_t (*spdylay_get_credential_cert)
(spdylay_session *session, const spdylay_origin *origin, size_t index,
uint8_t *cert, size_t certlen, void *user_data);
/**
* @struct
@ -1005,6 +1098,22 @@ typedef struct {
* received.
*/
spdylay_on_request_recv_callback on_request_recv_callback;
/**
* Callback function invoked when the library needs the
* cryptographic proof that the client has possession of the private
* key associated with the certificate.
*/
spdylay_get_credential_proof get_credential_proof;
/**
* Callback function invoked when the library needs the length of the
* client certificate chain.
*/
spdylay_get_credential_ncerts get_credential_ncerts;
/**
* Callback function invoked when the library needs the client
* certificate.
*/
spdylay_get_credential_cert get_credential_cert;
} spdylay_session_callbacks;
/**
@ -1073,6 +1182,49 @@ int spdylay_session_server_new(spdylay_session **session_ptr,
*/
void spdylay_session_del(spdylay_session *session);
/**
* @function
*
* Sets the origin tuple (|scheme|, |host| and |port) that the
* connection is made to and the client certificate is sent in the
* first TLS handshake. This function must be called before any call
* of `spdylay_session_send()` and `spdylay_session_recv()` and be
* called only once per session. This function must not be called if
* the |session| is initialized for server use. If the client did not
* provide the client certificate in the first TLS handshake, this
* function must not be called.
*
* This function stores the given origin at the slot 1 in the client
* certificate vector.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* SPDYLAY_ERR_NOMEM
* Out of memory
* SPDYLAY_ERR_INVALID_STATE
* The |session| is initialized for server use; or the client
* certificate vector size is 0.
*/
int spdylay_session_set_initial_client_cert_origin(spdylay_session *session,
const char *scheme,
const char *host,
uint16_t port);
/**
* @function
*
* Returns the origin at the index |slot| in the client certificate
* vector. If there is no origin at the given |slot|, this function
* returns ``NULL``.
*
* This function must not be called if the |session| is initialized
* for server use.
*/
const spdylay_origin* spdylay_session_get_client_cert_origin
(spdylay_session *session,
size_t slot);
/**
* @function
*
@ -1094,6 +1246,14 @@ void spdylay_session_del(spdylay_session *session);
* :member:`spdylay_session_callbacks.on_ctrl_not_send_callback` is
* invoked. Abort the following steps.
* 4. If the frame is SYN_STREAM, the stream is opened here.
* If the |session| is initialized for client use and the protocol
* version is :macro:`SPDYLAY_PROTO_SPDY3` and the library needs
* the client certificate for the origin,
* :member:`spdylay_session_callbacks.get_credential_ncerts` is
* invoked. If the result is more than zero,
* :member:`spdylay_session_callbacks.get_credential_proof` and
* :member:`spdylay_session_callbacks.get_credential_cert` are also
* invoked.
* 5. :member:`spdylay_session_callbacks.before_ctrl_send_callback` is
* invoked.
* 6. :member:`spdylay_session_callbacks.send_callback` is invoked one

View File

@ -0,0 +1,156 @@
/*
* Spdylay - SPDY Library
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "spdylay_client_cert_vector.h"
#include <string.h>
#include "spdylay_helper.h"
int spdylay_origin_equal(const spdylay_origin *lhs, const spdylay_origin *rhs)
{
return strcmp(lhs->scheme, rhs->scheme) == 0 &&
strcmp(lhs->host, rhs->host) == 0 && lhs->port == rhs->port;
}
int spdylay_origin_set(spdylay_origin *origin,
const char *scheme, const char *host, uint16_t port)
{
if(strlen(scheme) > SPDYLAY_MAX_SCHEME ||
strlen(host) > SPDYLAY_MAX_HOSTNAME) {
return SPDYLAY_ERR_INVALID_ARGUMENT;
}
strcpy(origin->scheme, scheme);
strcpy(origin->host, host);
origin->port = port;
return 0;
}
const char* spdylay_origin_get_scheme(const spdylay_origin *origin)
{
return origin->scheme;
}
const char* spdylay_origin_get_host(const spdylay_origin *origin)
{
return origin->host;
}
uint16_t spdylay_origin_get_port(const spdylay_origin *origin)
{
return origin->port;
}
int spdylay_client_cert_vector_init(spdylay_client_cert_vector *certvec,
size_t size)
{
certvec->size = certvec->capacity = size;
certvec->last_slot = 0;
if(certvec->capacity) {
size_t size = sizeof(spdylay_origin*)*certvec->capacity;
certvec->vector = malloc(size);
if(certvec->vector == NULL) {
return SPDYLAY_ERR_NOMEM;
}
memset(certvec->vector, 0, size);
} else {
certvec->vector = NULL;
}
return 0;
}
void spdylay_client_cert_vector_free(spdylay_client_cert_vector *certvec)
{
size_t i;
for(i = 0; i < certvec->size; ++i) {
free(certvec->vector[i]);
}
free(certvec->vector);
}
int spdylay_client_cert_vector_resize(spdylay_client_cert_vector *certvec,
size_t size)
{
if(certvec->capacity < size) {
spdylay_origin **vector = realloc(certvec->vector,
sizeof(spdylay_origin*)*size);
if(vector == NULL) {
return SPDYLAY_ERR_NOMEM;
}
memset(vector + certvec->capacity, 0,
sizeof(spdylay_origin*)*(size - certvec->capacity));
certvec->vector = vector;
certvec->size = certvec->capacity = size;
} else {
size_t i;
for(i = size; i < certvec->size; ++i) {
free(certvec->vector[i]);
certvec->vector[i] = NULL;
}
certvec->size = spdylay_min(certvec->size, size);
if(certvec->last_slot > certvec->size) {
certvec->last_slot = certvec->size;
}
}
return 0;
}
size_t spdylay_client_cert_vector_find(spdylay_client_cert_vector *certvec,
const spdylay_origin *origin)
{
size_t i;
for(i = 0; i < certvec->size && certvec->vector[i]; ++i) {
if(spdylay_origin_equal(origin, certvec->vector[i])) {
return i+1;
}
}
return 0;
}
size_t spdylay_client_cert_vector_put(spdylay_client_cert_vector *certvec,
spdylay_origin *origin)
{
if(certvec->size == 0) {
return 0;
}
if(certvec->last_slot == certvec->size) {
certvec->last_slot = 1;
} else {
++certvec->last_slot;
}
free(certvec->vector[certvec->last_slot-1]);
certvec->vector[certvec->last_slot-1] = origin;
return certvec->last_slot;
}
const spdylay_origin* spdylay_client_cert_vector_get_origin
(spdylay_client_cert_vector *certvec,
size_t slot)
{
if(slot == 0 || slot > certvec->size || !certvec->vector[slot-1]) {
return NULL;
} else {
return certvec->vector[slot-1];
}
}

View File

@ -0,0 +1,112 @@
/*
* Spdylay - SPDY Library
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SPDYLAY_CLIENT_CERT_VECTOR_H
#define SPDYLAY_CLIENT_CERT_VECTOR_H
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <spdylay/spdylay.h>
struct spdylay_origin {
char scheme[SPDYLAY_MAX_SCHEME+1];
char host[SPDYLAY_MAX_HOSTNAME+1];
uint16_t port;
};
typedef struct {
spdylay_origin **vector;
/* The size of the vector. */
size_t size;
/* The real capacity of the vector. size <= capacity holds true. */
size_t capacity;
/* The last slot where origin is stored. The default value is 0. */
size_t last_slot;
} spdylay_client_cert_vector;
/*
* Returns nonzero if |lhs| and |rhs| are equal. The equality is
* defined such that each member is equal respectively.
*/
int spdylay_origin_equal(const spdylay_origin *lhs, const spdylay_origin *rhs);
/*
* Convenient function to set members to the |origin|. The |origin|
* must be allocated prior this call.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* SPDYLAY_ERR_INVALID_ARGUMENT
* The |scheme| is longer than SPDYLAY_MAX_SCHEME; or the |host|
* is longer than SPDYLAY_MAX_HOSTNAME.
*/
int spdylay_origin_set(spdylay_origin *origin,
const char *scheme, const char *host, uint16_t port);
/*
* Initializes the client certificate vector with the vector size
* |size|.
*/
int spdylay_client_cert_vector_init(spdylay_client_cert_vector *certvec,
size_t size);
void spdylay_client_cert_vector_free(spdylay_client_cert_vector *certvec);
/*
* Returns the slot of the |origin| in the client certificate vector.
* If it is not found, returns 0.
*/
size_t spdylay_client_cert_vector_find(spdylay_client_cert_vector *certvec,
const spdylay_origin *origin);
/*
* Puts the |origin| to the |certvec|. This function takes ownership
* of the |origin| on success.
*
* This function returns the positive slot index of the certificate
* vector where the |origin| is stored if it succeeds, or 0.
*/
size_t spdylay_client_cert_vector_put(spdylay_client_cert_vector *certvec,
spdylay_origin *origin);
/*
* Resizes client certificate vector to the size |size|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* SPDYLAY_ERR_NOMEM
* Out of memory.
*/
int spdylay_client_cert_vector_resize(spdylay_client_cert_vector *certvec,
size_t size);
const spdylay_origin* spdylay_client_cert_vector_get_origin
(spdylay_client_cert_vector *certvec,
size_t slot);
#endif /* SPDYLAY_CLIENT_CERT_VECTOR_H */

View File

@ -27,6 +27,7 @@
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include "spdylay_helper.h"
#include "spdylay_net.h"
@ -500,6 +501,52 @@ void spdylay_frame_nv_2to3(char **nv)
}
}
#define SPDYLAY_HTTPS_PORT 443
int spdylay_frame_nv_set_origin(char **nv, spdylay_origin *origin)
{
int scheme_found, host_found;
int i;
scheme_found = host_found = 0;
for(i = 0; nv[i]; i += 2) {
if(scheme_found == 0 && strcmp(":scheme", nv[i]) == 0) {
size_t len = strlen(nv[i+1]);
if(len <= SPDYLAY_MAX_SCHEME) {
strcpy(origin->scheme, nv[i+1]);
scheme_found = 1;
}
} else if(host_found == 0 && strcmp(":host", nv[i]) == 0) {
size_t len = strlen(nv[i+1]);
char *sep = memchr(nv[i+1], ':', len);
size_t hostlen;
if(sep == NULL) {
origin->port = SPDYLAY_HTTPS_PORT;
sep = nv[i+1]+len;
} else {
unsigned long int port;
errno = 0;
port = strtoul(sep+1, NULL, 10);
if(errno != 0 || port == 0 || port > UINT16_MAX) {
continue;
}
origin->port = port;
}
hostlen = sep-nv[i+1];
if(hostlen > SPDYLAY_MAX_HOSTNAME) {
continue;
}
memcpy(origin->host, nv[i+1], hostlen);
origin->host[hostlen] = '\0';
host_found = 1;
}
}
if(scheme_found && host_found) {
return 0;
} else {
return SPDYLAY_ERR_INVALID_ARGUMENT;
}
}
void spdylay_frame_syn_stream_init(spdylay_syn_stream *frame,
uint16_t version, uint8_t flags,
int32_t stream_id, int32_t assoc_stream_id,
@ -640,6 +687,36 @@ void spdylay_frame_settings_free(spdylay_settings *frame)
free(frame->iv);
}
void spdylay_frame_credential_init(spdylay_credential *frame,
uint16_t version, uint16_t slot,
spdylay_mem_chunk *proof,
spdylay_mem_chunk *certs,
size_t ncerts)
{
size_t i;
memset(frame, 0, sizeof(spdylay_credential));
frame->hd.version = version;
frame->hd.type = SPDYLAY_CREDENTIAL;
frame->slot = slot;
frame->proof = *proof;
frame->certs = certs;
frame->ncerts = ncerts;
frame->hd.length = 2+4+frame->proof.length;
for(i = 0; i < ncerts; ++i) {
frame->hd.length += 4+frame->certs[i].length;
}
}
void spdylay_frame_credential_free(spdylay_credential *frame)
{
size_t i;
free(frame->proof.data);
for(i = 0; i < frame->ncerts; ++i) {
free(frame->certs[i].data);
}
free(frame->certs);
}
void spdylay_frame_data_init(spdylay_data *frame, int32_t stream_id,
uint8_t flags,
const spdylay_data_provider *data_prd)
@ -1076,6 +1153,158 @@ int spdylay_frame_unpack_settings(spdylay_settings *frame,
return 0;
}
ssize_t spdylay_frame_pack_credential(uint8_t **buf_ptr, size_t *buflen_ptr,
spdylay_credential *frame)
{
ssize_t framelen;
int r;
size_t i, offset;
framelen = SPDYLAY_FRAME_HEAD_LENGTH+2+4+frame->proof.length;
for(i = 0; i < frame->ncerts; ++i) {
framelen += 4+frame->certs[i].length;
}
r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen);
if(r != 0) {
return r;
}
memset(*buf_ptr, 0, framelen);
spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);
offset = SPDYLAY_FRAME_HEAD_LENGTH;
spdylay_put_uint16be(&(*buf_ptr)[offset], frame->slot);
offset += 2;
spdylay_put_uint32be(&(*buf_ptr)[offset], frame->proof.length);
offset += 4;
memcpy(&(*buf_ptr)[offset], frame->proof.data, frame->proof.length);
offset += frame->proof.length;
for(i = 0; i < frame->ncerts; ++i) {
spdylay_put_uint32be(&(*buf_ptr)[offset], frame->certs[i].length);
offset += 4;
memcpy(&(*buf_ptr)[offset], frame->certs[i].data, frame->certs[i].length);
offset += frame->certs[i].length;
}
return framelen;
}
/*
* Counts number of client certificate in CREDENTIAL frame payload
* |payload| with length |payloadlen|. The |payload| points to the
* length of the first certificate. This function also checks the
* frame payload is properly composed.
*
* This function returns the number of certificates in |payload| if it
* succeeds, or one of the following negative error codes:
*
* SPDYLAY_ERR_INVALID_FRAME
* The frame payload is invalid.
*/
static int spdylay_frame_count_unpack_cert(const uint8_t *payload,
size_t payloadlen)
{
size_t n, offset = 0;
for(n = 1; 1; ++n) {
size_t len;
if(offset+4 > payloadlen) {
return SPDYLAY_ERR_INVALID_FRAME;
}
len = spdylay_get_uint32(&payload[offset]);
offset += 4;
if(len > payloadlen || offset+len > payloadlen) {
return SPDYLAY_ERR_INVALID_FRAME;
} else {
offset += len;
if(offset == payloadlen) {
break;
}
}
}
return n;
}
/*
* Unpacks client certificates in the |payload| with length
* |payloadlen|. First allocates memory to store the |ncerts|
* certificates. Stores certificates in the allocated space and set
* its pointer to |*certs_ptr|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* SPDYLAY_ERR_NOMEM
* Out of memory
*/
static int spdylay_frame_unpack_cert(spdylay_mem_chunk **certs_ptr,
size_t ncerts,
const uint8_t *payload, size_t payloadlen)
{
size_t offset, i, j;
spdylay_mem_chunk *certs;
certs = malloc(sizeof(spdylay_mem_chunk)*ncerts);
if(certs == NULL) {
return SPDYLAY_ERR_NOMEM;
}
offset = 0;
for(i = 0; i < ncerts; ++i) {
certs[i].length = spdylay_get_uint32(&payload[offset]);
offset += 4;
certs[i].data = malloc(certs[i].length);
if(certs[i].data == NULL) {
goto fail;
}
memcpy(certs[i].data, &payload[offset], certs[i].length);
offset += certs[i].length;
}
*certs_ptr = certs;
return 0;
fail:
for(j = 0; j < i; ++j) {
free(certs[j].data);
}
free(certs);
return SPDYLAY_ERR_NOMEM;
}
int spdylay_frame_unpack_credential(spdylay_credential *frame,
const uint8_t *head, size_t headlen,
const uint8_t *payload,
size_t payloadlen)
{
size_t offset;
int rv;
if(payloadlen < 10) {
return SPDYLAY_ERR_INVALID_FRAME;
}
spdylay_frame_unpack_ctrl_hd(&frame->hd, head);
offset = 0;
frame->slot = spdylay_get_uint16(&payload[offset]);
offset += 2;
frame->proof.length = spdylay_get_uint32(&payload[offset]);
offset += 4;
if(frame->proof.length > payloadlen ||
offset+frame->proof.length > payloadlen) {
return SPDYLAY_ERR_INVALID_FRAME;
}
frame->proof.data = malloc(frame->proof.length);
if(frame->proof.data == NULL) {
return SPDYLAY_ERR_NOMEM;
}
memcpy(frame->proof.data, &payload[offset], frame->proof.length);
offset += frame->proof.length;
rv = spdylay_frame_count_unpack_cert(payload+offset, payloadlen-offset);
if(rv < 0) {
goto fail;
}
frame->ncerts = rv;
rv = spdylay_frame_unpack_cert(&frame->certs, frame->ncerts,
payload+offset, payloadlen-offset);
if(rv != 0) {
goto fail;
}
return 0;
fail:
free(frame->proof.data);
return rv;
}
spdylay_settings_entry* spdylay_frame_iv_copy(const spdylay_settings_entry *iv,
size_t niv)
{

View File

@ -32,6 +32,7 @@
#include <spdylay/spdylay.h>
#include "spdylay_zlib.h"
#include "spdylay_buffer.h"
#include "spdylay_client_cert_vector.h"
#define SPDYLAY_STREAM_ID_MASK 0x7fffffff
#define SPDYLAY_LENGTH_MASK 0xffffff
@ -443,6 +444,35 @@ int spdylay_frame_unpack_settings(spdylay_settings *frame,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen);
/*
* Packs CREDENTIAL frame |frame| in wire format and store it in
* |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|
* length. This function expands |*buf_ptr| as necessary to store
* given |frame|.
*
* This function returns the size of packed frame if it succeeds, or
* returns one of the following negative error codes:
*
* SPDYLAY_ERR_NOMEM
* Out of memory.
*/
ssize_t spdylay_frame_pack_credential(uint8_t **buf_ptr, size_t *buflen_ptr,
spdylay_credential *frame);
/*
* Unpacks CREDENTIAL wire format into |frame|.
*
* This function returns 0 if it succeeds or one of the following
* negative error codes:
*
* SPDYLAY_ERR_INVALID_FRAME
* The input data are invalid.
* SPDYLAY_ERR_NOMEM
* Out of memory.
*/
int spdylay_frame_unpack_credential(spdylay_credential *frame,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen);
/*
* Returns number of bytes to pack name/value pairs |nv|. This
* function expects |nv| is sorted in ascending order of key.
@ -661,6 +691,19 @@ void spdylay_frame_settings_init(spdylay_settings *frame,
void spdylay_frame_settings_free(spdylay_settings *frame);
/*
* Initializes CREDENTIAL frame |frame| with given values. This
* function takes ownership of |proof->data| and |certs| on success.
* Note that the ownership of |proof| is not taken.
*/
void spdylay_frame_credential_init(spdylay_credential *frame,
uint16_t version, uint16_t slot,
spdylay_mem_chunk *proof,
spdylay_mem_chunk *certs,
size_t ncerts);
void spdylay_frame_credential_free(spdylay_credential *frame);
void spdylay_frame_data_init(spdylay_data *frame, int32_t stream_id,
uint8_t flags,
const spdylay_data_provider *data_prd);
@ -716,6 +759,24 @@ void spdylay_frame_nv_3to2(char **nv);
*/
void spdylay_frame_nv_2to3(char **nv);
/*
* Assigns the members of the |origin| using ":scheme" and ":host"
* values in |nv|.
*
* If ":host" value contains ':', this function parses the chracters
* after ':' as integer and uses it as port number.
*
* If ':' is missing in :host value, the default port number is used.
* The only defined default port number is 443.
*
* This function returns 0 if it succeeds, or one of the following
* negative error code:
*
* SPDYLAY_ERR_INVALID_ARGUMENT
* The |nv| lacks either :scheme or :host, or both.
*/
int spdylay_frame_nv_set_origin(char **nv, spdylay_origin *origin);
/*
* Makes copy of |iv| and return the copy. The |niv| is the number of
* entries in |iv|. This function returns the pointer to the copy if

View File

@ -35,4 +35,10 @@
typedef int (*spdylay_compar)(const void *lhs, const void *rhs);
/* Internal error code. They must be in the range [-499, -100],
inclusive. */
typedef enum {
SPDYLAY_ERR_CREDENTIAL_PENDING = -100
} spdylay_internal_error;
#endif /* SPDYLAY_INT_H */

View File

@ -66,6 +66,9 @@ void spdylay_outbound_item_free(spdylay_outbound_item *item)
case SPDYLAY_WINDOW_UPDATE:
spdylay_frame_window_update_free(&frame->window_update);
break;
case SPDYLAY_CREDENTIAL:
spdylay_frame_credential_free(&frame->credential);
break;
}
} else if(item->frame_cat == SPDYLAY_DATA) {
spdylay_data *data_frame;

View File

@ -32,6 +32,13 @@
#include <spdylay/spdylay.h>
#include "spdylay_frame.h"
/* Priority for PING */
#define SPDYLAY_OB_PRI_PING -10
/* Priority for CREDENTIAL */
#define SPDYLAY_OB_PRI_CREDENTIAL -2
/* Priority for the frame which must be sent after CREDENTIAL */
#define SPDYLAY_OB_PRI_AFTER_CREDENTIAL -1
typedef struct {
spdylay_data_provider *data_prd;
void *stream_user_data;

View File

@ -114,7 +114,8 @@ static void spdylay_inbound_frame_reset(spdylay_inbound_frame *iframe)
static int spdylay_session_new(spdylay_session **session_ptr,
uint16_t version,
const spdylay_session_callbacks *callbacks,
void *user_data)
void *user_data,
size_t cli_certvec_length)
{
int r;
if(version != SPDYLAY_PROTO_SPDY2 && version != SPDYLAY_PROTO_SPDY3) {
@ -205,8 +206,16 @@ static int spdylay_session_new(spdylay_session **session_ptr,
(*session_ptr)->iframe.bufmax = SPDYLAY_INITIAL_INBOUND_FRAMEBUF_LENGTH;
spdylay_inbound_frame_reset(&(*session_ptr)->iframe);
r = spdylay_client_cert_vector_init(&(*session_ptr)->cli_certvec,
cli_certvec_length);
if(r != 0) {
goto fail_client_cert_vector;
}
return 0;
fail_client_cert_vector:
free((*session_ptr)->iframe.buf);
fail_iframe_buf:
free((*session_ptr)->nvbuf);
fail_nvbuf:
@ -232,7 +241,8 @@ int spdylay_session_client_new(spdylay_session **session_ptr,
void *user_data)
{
int r;
r = spdylay_session_new(session_ptr, version, callbacks, user_data);
r = spdylay_session_new(session_ptr, version, callbacks, user_data,
SPDYLAY_INITIAL_CLIENT_CERT_VECTOR_LENGTH);
if(r == 0) {
/* IDs for use in client */
(*session_ptr)->next_stream_id = 1;
@ -248,7 +258,8 @@ int spdylay_session_server_new(spdylay_session **session_ptr,
void *user_data)
{
int r;
r = spdylay_session_new(session_ptr, version, callbacks, user_data);
r = spdylay_session_new(session_ptr, version, callbacks, user_data,
0);
if(r == 0) {
(*session_ptr)->server = 1;
/* IDs for use in client */
@ -301,6 +312,7 @@ void spdylay_session_del(spdylay_session *session)
free(session->nvbuf);
spdylay_buffer_free(&session->inflatebuf);
free(session->iframe.buf);
spdylay_client_cert_vector_free(&session->cli_certvec);
free(session);
}
@ -354,8 +366,8 @@ int spdylay_session_add_frame(spdylay_session *session,
unreachable. */
assert(0);
case SPDYLAY_PING:
/* Ping has "height" priority. Give it -1. */
item->pri = -1;
/* Ping has highest priority. */
item->pri = SPDYLAY_OB_PRI_PING;
break;
case SPDYLAY_GOAWAY:
/* Should GOAWAY have higher priority? */
@ -376,6 +388,9 @@ int spdylay_session_add_frame(spdylay_session *session,
}
break;
}
case SPDYLAY_CREDENTIAL:
item->pri = SPDYLAY_OB_PRI_CREDENTIAL;
break;
}
if(frame_type == SPDYLAY_SYN_STREAM) {
r = spdylay_pq_push(&session->ob_ss_pq, item);
@ -732,6 +747,146 @@ static int spdylay_session_predicate_data_send(spdylay_session *session,
}
}
/*
* Retrieves cryptographic proof for the given |origin| using callback
* function and store it in |proof|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* SPDYLAY_ERR_NOMEM
* Out of memory.
*/
static int spdylay_session_get_credential_proof(spdylay_session *session,
const spdylay_origin *origin,
spdylay_mem_chunk *proof)
{
proof->length = session->callbacks.get_credential_proof(session,
origin,
NULL, 0,
session->user_data);
proof->data = malloc(proof->length);
if(proof->data == NULL) {
return SPDYLAY_ERR_NOMEM;
}
session->callbacks.get_credential_proof(session, origin,
proof->data, proof->length,
session->user_data);
return 0;
}
/*
* Retrieves client certificate chain for the given |origin| using
* callback functions and store the pointer to the allocated buffer
* containing certificate chain to |*certs_ptr|. The length of
* certificate chain is exactly |ncerts|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* SPDYLAY_ERR_NOMEM
* Out of memory.
*/
static int spdylay_session_get_credential_cert(spdylay_session *session,
const spdylay_origin *origin,
spdylay_mem_chunk **certs_ptr,
size_t ncerts)
{
spdylay_mem_chunk *certs;
size_t i, j;
certs = malloc(sizeof(spdylay_mem_chunk)*ncerts);
if(certs == NULL) {
return SPDYLAY_ERR_NOMEM;
}
for(i = 0; i < ncerts; ++i) {
certs[i].length = session->callbacks.get_credential_cert
(session,
origin,
i, NULL, 0,
session->user_data);
certs[i].data = malloc(certs[i].length);
if(certs[i].data == NULL) {
goto fail;
}
session->callbacks.get_credential_cert(session, origin, i,
certs[i].data, certs[i].length,
session->user_data);
}
*certs_ptr = certs;
return 0;
fail:
for(j = 0; j < i; ++j) {
free(certs[i].data);
}
free(certs);
return SPDYLAY_ERR_NOMEM;
}
int spdylay_session_prep_credential(spdylay_session *session,
spdylay_syn_stream *syn_stream)
{
int rv;
spdylay_origin origin;
spdylay_frame *frame;
spdylay_mem_chunk proof;
if(session->cli_certvec.size == 0) {
return 0;
}
rv = spdylay_frame_nv_set_origin(syn_stream->nv, &origin);
if(rv == 0) {
size_t slot;
slot = spdylay_client_cert_vector_find(&session->cli_certvec, &origin);
if(slot == 0) {
ssize_t ncerts;
ncerts = session->callbacks.get_credential_ncerts(session, &origin,
session->user_data);
if(ncerts > 0) {
spdylay_mem_chunk *certs;
spdylay_origin *origin_copy;
frame = malloc(sizeof(spdylay_frame));
if(frame == NULL) {
return SPDYLAY_ERR_NOMEM;
}
origin_copy = malloc(sizeof(spdylay_origin));
if(origin_copy == NULL) {
goto fail_after_frame;
}
*origin_copy = origin;
slot = spdylay_client_cert_vector_put(&session->cli_certvec,
origin_copy);
rv = spdylay_session_get_credential_proof(session, &origin, &proof);
if(rv != 0) {
goto fail_after_frame;
}
rv = spdylay_session_get_credential_cert(session, &origin,
&certs, ncerts);
if(rv != 0) {
goto fail_after_proof;
}
spdylay_frame_credential_init(&frame->credential,
session->version,
slot, &proof, certs, ncerts);
rv = spdylay_session_add_frame(session, SPDYLAY_CTRL, frame, NULL);
if(rv != 0) {
spdylay_frame_credential_free(&frame->credential);
return rv;
}
return SPDYLAY_ERR_CREDENTIAL_PENDING;
}
} else {
return slot;
}
}
return 0;
fail_after_proof:
free(proof.data);
fail_after_frame:
free(frame);
return rv;
}
static ssize_t spdylay_session_prep_frame(spdylay_session *session,
spdylay_outbound_item *item)
{
@ -751,8 +906,30 @@ static ssize_t spdylay_session_prep_frame(spdylay_session *session,
if(r != 0) {
return r;
}
if(session->version == SPDYLAY_PROTO_SPDY3 &&
session->callbacks.get_credential_proof &&
session->callbacks.get_credential_cert &&
!session->server) {
int slot_index;
slot_index = spdylay_session_prep_credential(session,
&frame->syn_stream);
if(slot_index == SPDYLAY_ERR_CREDENTIAL_PENDING) {
/* CREDENTIAL frame has been queued. SYN_STREAM has to be
sent after that. Change the priority of this item to
achieve this. */
item->pri = SPDYLAY_OB_PRI_AFTER_CREDENTIAL;
r = spdylay_pq_push(&session->ob_ss_pq, item);
if(r == 0) {
return SPDYLAY_ERR_CREDENTIAL_PENDING;
} else {
return r;
}
} else if(r < 0) {
return r;
}
frame->syn_stream.slot = slot_index;
}
stream_id = session->next_stream_id;
frame->syn_stream.stream_id = stream_id;
session->next_stream_id += 2;
if(session->version == SPDYLAY_PROTO_SPDY2) {
@ -892,6 +1069,15 @@ static ssize_t spdylay_session_prep_frame(spdylay_session *session,
return framebuflen;
}
break;
case SPDYLAY_CREDENTIAL: {
framebuflen = spdylay_frame_pack_credential(&session->aob.framebuf,
&session->aob.framebufmax,
&frame->credential);
if(framebuflen < 0) {
return framebuflen;
}
break;
}
default:
framebuflen = SPDYLAY_ERR_INVALID_ARGUMENT;
}
@ -1134,6 +1320,8 @@ static int spdylay_session_after_frame_sent(spdylay_session *session)
}
case SPDYLAY_WINDOW_UPDATE:
break;
case SPDYLAY_CREDENTIAL:
break;
}
spdylay_active_outbound_item_reset(&session->aob);
} else if(item->frame_cat == SPDYLAY_DATA) {
@ -1235,7 +1423,8 @@ int spdylay_session_send(spdylay_session *session)
break;
}
framebuflen = spdylay_session_prep_frame(session, item);
if(framebuflen == SPDYLAY_ERR_DEFERRED) {
if(framebuflen == SPDYLAY_ERR_DEFERRED ||
framebuflen == SPDYLAY_ERR_CREDENTIAL_PENDING) {
continue;
} else if(framebuflen < 0) {
if(item->frame_cat == SPDYLAY_CTRL &&
@ -1624,7 +1813,7 @@ int spdylay_session_on_settings_received(spdylay_session *session,
/* Check ID/value pairs and persist them if necessary. */
memset(check, 0, sizeof(check));
for(i = 0; i < frame->settings.niv; ++i) {
const spdylay_settings_entry *entry = &frame->settings.iv[i];
spdylay_settings_entry *entry = &frame->settings.iv[i];
/* SPDY/3 spec says if the multiple values for the same ID were
found, use the first one and ignore the rest. */
if(entry->settings_id > SPDYLAY_SETTINGS_MAX || entry->settings_id == 0 ||
@ -1639,6 +1828,19 @@ int spdylay_session_on_settings_received(spdylay_session *session,
if(entry->value < (1u << 31)) {
spdylay_session_update_initial_window_size(session, entry->value);
}
} else if(entry->settings_id ==
SPDYLAY_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE) {
if(!session->server) {
int rv;
/* Limit certificate vector length in the reasonable size. */
entry->value = spdylay_min(entry->value,
SPDYLAY_MAX_CLIENT_CERT_VECTOR_LENGTH);
rv = spdylay_client_cert_vector_resize(&session->cli_certvec,
entry->value);
if(rv != 0) {
return rv;
}
}
}
session->remote_settings[entry->settings_id] = entry->value;
}
@ -1721,6 +1923,19 @@ int spdylay_session_on_window_update_received(spdylay_session *session,
return 0;
}
int spdylay_session_on_credential_received(spdylay_session *session,
spdylay_frame *frame)
{
if(!spdylay_session_check_version(session, frame->credential.hd.version)) {
return 0;
}
/* We don't care about the body of the CREDENTIAL frame. It is left
to the application code to decide it is invalid or not. */
spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_CREDENTIAL,
frame);
return 0;
}
int spdylay_session_on_headers_received(spdylay_session *session,
spdylay_frame *frame)
{
@ -1930,6 +2145,19 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);
}
break;
case SPDYLAY_CREDENTIAL:
r = spdylay_frame_unpack_credential(&frame.credential,
session->iframe.headbuf,
sizeof(session->iframe.headbuf),
session->iframe.buf,
session->iframe.len);
if(r == 0) {
r = spdylay_session_on_credential_received(session, &frame);
spdylay_frame_credential_free(&frame.credential);
} else if(spdylay_is_non_fatal(r)) {
r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);
}
break;
}
if(spdylay_is_fatal(r)) {
return r;
@ -2360,3 +2588,38 @@ size_t spdylay_session_get_outbound_queue_size(spdylay_session *session)
{
return spdylay_pq_size(&session->ob_pq)+spdylay_pq_size(&session->ob_ss_pq);
}
int spdylay_session_set_initial_client_cert_origin(spdylay_session *session,
const char *scheme,
const char *host,
uint16_t port)
{
spdylay_origin *origin;
size_t slot;
if(strlen(scheme) > SPDYLAY_MAX_SCHEME ||
strlen(host) > SPDYLAY_MAX_HOSTNAME) {
return SPDYLAY_ERR_INVALID_ARGUMENT;
}
if(session->server ||
(session->cli_certvec.size == 0 ||
session->cli_certvec.last_slot != 0)) {
return SPDYLAY_ERR_INVALID_STATE;
}
origin = malloc(sizeof(spdylay_origin));
if(origin == NULL) {
return SPDYLAY_ERR_NOMEM;
}
strcpy(origin->scheme, scheme);
strcpy(origin->host, host);
origin->port = port;
slot = spdylay_client_cert_vector_put(&session->cli_certvec, origin);
assert(slot == 1);
return 0;
}
const spdylay_origin* spdylay_session_get_client_cert_origin
(spdylay_session *session,
size_t slot)
{
return spdylay_client_cert_vector_get_origin(&session->cli_certvec, slot);
}

View File

@ -37,6 +37,7 @@
#include "spdylay_stream.h"
#include "spdylay_buffer.h"
#include "spdylay_outbound_item.h"
#include "spdylay_client_cert_vector.h"
/**
* @macro
@ -74,6 +75,11 @@ typedef struct {
#define SPDYLAY_INITIAL_WINDOW_SIZE 65536
/* Initial size of client certificate vector */
#define SPDYLAY_INITIAL_CLIENT_CERT_VECTOR_LENGTH 8
/* Maxmum size of client certificate vector */
#define SPDYLAY_MAX_CLIENT_CERT_VECTOR_LENGTH 255
typedef enum {
SPDYLAY_RECV_HEAD,
SPDYLAY_RECV_PAYLOAD
@ -167,6 +173,9 @@ struct spdylay_session {
/* Settings value of the local endpoint. */
uint32_t local_settings[SPDYLAY_SETTINGS_MAX+1];
/* Client certificate vector */
spdylay_client_cert_vector cli_certvec;
spdylay_session_callbacks callbacks;
void *user_data;
};
@ -411,6 +420,15 @@ int spdylay_session_on_headers_received(spdylay_session *session,
int spdylay_session_on_window_update_received(spdylay_session *session,
spdylay_frame *frame);
/*
* Called when CREDENTIAL is received, assuming |frame.credential| is
* properly initialized.
*
* Currently, this function always succeeds and returns 0.
*/
int spdylay_session_on_credential_received(spdylay_session *session,
spdylay_frame *frame);
/*
* Called when DATA is received.
*
@ -495,4 +513,21 @@ void spdylay_session_update_local_settings(spdylay_session *session,
spdylay_settings_entry *iv,
size_t niv);
/*
* Returns the index in the client certificate vector for the
* |syn_stream|. The origin is computed from |syn_stream->nv|. If no
* client certificate is required, return 0. If CREDENTIAL frame needs
* to be sent before the |syn_stream|, this function returns
* :macro:`SPDYLAY_ERR_CREDENTIAL_PENDING`. In this case, CREDENTIAL
* frame has been already queued. This function returns one of the
* following negative error codes:
*
* SPDYLAY_ERR_NOMEM
* Out of memory.
* SPDYLAY_ERR_CREDENTIAL_PENDING
* The CREDENTIAL frame must be sent before the |syn_stream|.
*/
int spdylay_session_prep_credential(spdylay_session *session,
spdylay_syn_stream *syn_stream);
#endif /* SPDYLAY_SESSION_H */

View File

@ -27,11 +27,13 @@ check_PROGRAMS = main
OBJECTS = main.c spdylay_pq_test.c spdylay_map_test.c spdylay_queue_test.c \
spdylay_buffer_test.c spdylay_zlib_test.c spdylay_session_test.c \
spdylay_frame_test.c spdylay_stream_test.c spdylay_npn_test.c
spdylay_frame_test.c spdylay_stream_test.c spdylay_npn_test.c \
spdylay_client_cert_vector_test.c
HFILES = spdylay_pq_test.h spdylay_map_test.h spdylay_queue_test.h \
spdylay_buffer_test.h spdylay_zlib_test.h spdylay_session_test.h \
spdylay_frame_test.h spdylay_stream_test.h spdylay_npn_test.h
spdylay_frame_test.h spdylay_stream_test.h spdylay_npn_test.h \
spdylay_client_cert_vector_test.h
main_SOURCES = $(HFILES) $(OBJECTS)

View File

@ -35,6 +35,7 @@
#include "spdylay_frame_test.h"
#include "spdylay_stream_test.h"
#include "spdylay_npn_test.h"
#include "spdylay_client_cert_vector_test.h"
static int init_suite1(void)
{
@ -144,6 +145,12 @@ int main(int argc, char* argv[])
test_spdylay_submit_settings) ||
!CU_add_test(pSuite, "session_get_outbound_queue_size",
test_spdylay_session_get_outbound_queue_size) ||
!CU_add_test(pSuite, "session_prep_credential",
test_spdylay_session_prep_credential) ||
!CU_add_test(pSuite, "session_submit_syn_stream_with_credential",
test_spdylay_submit_syn_stream_with_credential) ||
!CU_add_test(pSuite, "session_set_initial_client_cert_origin",
test_spdylay_session_set_initial_client_cert_origin) ||
!CU_add_test(pSuite, "frame_unpack_nv_spdy2",
test_spdylay_frame_unpack_nv_spdy2) ||
!CU_add_test(pSuite, "frame_unpack_nv_spdy3",
@ -175,6 +182,8 @@ int main(int argc, char* argv[])
test_spdylay_frame_pack_settings_spdy2) ||
!CU_add_test(pSuite, "frame_pack_settings_spdy3",
test_spdylay_frame_pack_settings_spdy3) ||
!CU_add_test(pSuite, "frame_pack_credential",
test_spdylay_frame_pack_credential) ||
!CU_add_test(pSuite, "frame_nv_sort", test_spdylay_frame_nv_sort) ||
!CU_add_test(pSuite, "frame_nv_downcase",
test_spdylay_frame_nv_downcase) ||
@ -186,8 +195,16 @@ int main(int argc, char* argv[])
test_spdylay_frame_unpack_nv_check_name_spdy2) ||
!CU_add_test(pSuite, "frame_unpack_nv_check_name_spdy3",
test_spdylay_frame_unpack_nv_check_name_spdy3) ||
!CU_add_test(pSuite, "frame_nv_set_origin",
test_spdylay_frame_nv_set_origin) ||
!CU_add_test(pSuite, "stream_add_pushed_stream",
test_spdylay_stream_add_pushed_stream)) {
test_spdylay_stream_add_pushed_stream) ||
!CU_add_test(pSuite, "client_cert_vector_find",
test_spdylay_client_cert_vector_find) ||
!CU_add_test(pSuite, "client_cert_vector_resize",
test_spdylay_client_cert_vector_resize) ||
!CU_add_test(pSuite, "client_cert_vector_get_origin",
test_spdylay_client_cert_vector_get_origin)) {
CU_cleanup_registry();
return CU_get_error();
}

View File

@ -0,0 +1,124 @@
/*
* Spdylay - SPDY Library
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "spdylay_client_cert_vector_test.h"
#include <CUnit/CUnit.h>
#include "spdylay_client_cert_vector.h"
static spdylay_origin* create_origin(const char *scheme, const char *host,
uint16_t port)
{
spdylay_origin *origin = malloc(sizeof(spdylay_origin));
spdylay_origin_set(origin, scheme, host, port);
return origin;
}
void test_spdylay_client_cert_vector_find(void)
{
spdylay_client_cert_vector certvec;
spdylay_origin *origin;
const spdylay_origin *origin_get;
size_t slot;
spdylay_client_cert_vector_init(&certvec, 3);
origin = create_origin("https", "example.org", 443);
CU_ASSERT(0 == spdylay_client_cert_vector_find(&certvec, origin));
CU_ASSERT(1 == spdylay_client_cert_vector_put(&certvec, origin));
slot = spdylay_client_cert_vector_find(&certvec, origin);
CU_ASSERT(1 == slot);
origin_get = spdylay_client_cert_vector_get_origin(&certvec, slot);
CU_ASSERT(strcmp(origin->scheme, origin_get->scheme) == 0);
CU_ASSERT(strcmp(origin->host, origin_get->host) == 0);
CU_ASSERT(origin->port == origin_get->port);
origin = create_origin("https", "example.org", 8443);
CU_ASSERT(0 == spdylay_client_cert_vector_find(&certvec, origin));
CU_ASSERT(2 == spdylay_client_cert_vector_put(&certvec, origin));
slot = spdylay_client_cert_vector_find(&certvec, origin);
CU_ASSERT(2 == slot);
origin = create_origin("https", "example.com", 443);
CU_ASSERT(0 == spdylay_client_cert_vector_find(&certvec, origin));
CU_ASSERT(3 == spdylay_client_cert_vector_put(&certvec, origin));
slot = spdylay_client_cert_vector_find(&certvec, origin);
CU_ASSERT(3 == slot);
origin = create_origin("https", "example.com", 8443);
CU_ASSERT(0 == spdylay_client_cert_vector_find(&certvec, origin));
CU_ASSERT(1 == spdylay_client_cert_vector_put(&certvec, origin));
slot = spdylay_client_cert_vector_find(&certvec, origin);
CU_ASSERT(1 == slot);
origin = create_origin("https", "example.org", 443);
CU_ASSERT(0 == spdylay_client_cert_vector_find(&certvec, origin));
free(origin);
spdylay_client_cert_vector_free(&certvec);
}
void test_spdylay_client_cert_vector_resize(void)
{
spdylay_client_cert_vector certvec;
spdylay_origin *origin;
size_t i;
spdylay_client_cert_vector_init(&certvec, 3);
origin = create_origin("https", "example.org", 443);
spdylay_client_cert_vector_put(&certvec, origin);
origin = create_origin("https", "example.com", 443);
spdylay_client_cert_vector_put(&certvec, origin);
CU_ASSERT(0 == spdylay_client_cert_vector_resize(&certvec, 1));
CU_ASSERT(NULL != spdylay_client_cert_vector_get_origin(&certvec, 1));
CU_ASSERT(1 == certvec.last_slot);
CU_ASSERT(0 == spdylay_client_cert_vector_resize(&certvec, 8));
CU_ASSERT(NULL != spdylay_client_cert_vector_get_origin(&certvec, 1));
CU_ASSERT(1 == certvec.last_slot);
for(i = 2; i <= 8; ++i) {
CU_ASSERT(NULL == spdylay_client_cert_vector_get_origin(&certvec, i));
}
spdylay_client_cert_vector_free(&certvec);
}
void test_spdylay_client_cert_vector_get_origin(void)
{
spdylay_client_cert_vector certvec;
spdylay_origin *origin;
spdylay_client_cert_vector_init(&certvec, 3);
origin = create_origin("https", "example.org", 443);
CU_ASSERT(1 == spdylay_client_cert_vector_put(&certvec, origin));
CU_ASSERT(NULL == spdylay_client_cert_vector_get_origin(&certvec, 0));
CU_ASSERT(NULL != spdylay_client_cert_vector_get_origin(&certvec, 1));
CU_ASSERT(NULL == spdylay_client_cert_vector_get_origin(&certvec, 2));
CU_ASSERT(NULL == spdylay_client_cert_vector_get_origin(&certvec, 3));
CU_ASSERT(NULL == spdylay_client_cert_vector_get_origin(&certvec, 4));
spdylay_client_cert_vector_free(&certvec);
}

View File

@ -0,0 +1,32 @@
/*
* Spdylay - SPDY Library
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SPDYLAY_CLIENT_CERT_VECTOR_TEST_H
#define SPDYLAY_CLIENT_CERT_VECTOR_TEST_H
void test_spdylay_client_cert_vector_find(void);
void test_spdylay_client_cert_vector_resize(void);
void test_spdylay_client_cert_vector_get_origin(void);
#endif /* SPDYLAY_CLIENT_CERT_VECTOR_TEST_H */

View File

@ -517,6 +517,66 @@ void test_spdylay_frame_pack_settings_spdy3(void)
test_spdylay_frame_pack_settings_version(SPDYLAY_PROTO_SPDY3);
}
static char* strcopy(const char* s)
{
size_t len = strlen(s);
char *dest = malloc(len+1);
memcpy(dest, s, len);
dest[len] = '\0';
return dest;
}
void test_spdylay_frame_pack_credential(void)
{
spdylay_frame frame, oframe;
uint8_t *buf = NULL;
size_t buflen = 0;
ssize_t framelen;
spdylay_mem_chunk proof;
spdylay_mem_chunk *certs;
size_t ncerts;
proof.data = (uint8_t*)strcopy("PROOF");
proof.length = strlen("PROOF");
ncerts = 2;
certs = malloc(sizeof(spdylay_mem_chunk)*ncerts);
certs[0].data = (uint8_t*)strcopy("CERT0");
certs[0].length = strlen("CERT0");
certs[1].data = (uint8_t*)strcopy("CERT1");
certs[1].length = strlen("CERT1");
spdylay_frame_credential_init(&frame.credential, SPDYLAY_PROTO_SPDY3,
1, &proof, certs, ncerts);
framelen = spdylay_frame_pack_credential(&buf, &buflen, &frame.credential);
CU_ASSERT(0 == spdylay_frame_unpack_credential
(&oframe.credential,
&buf[0], SPDYLAY_FRAME_HEAD_LENGTH,
&buf[SPDYLAY_FRAME_HEAD_LENGTH],
framelen-SPDYLAY_FRAME_HEAD_LENGTH));
CU_ASSERT(1 == oframe.credential.slot);
CU_ASSERT(5 == oframe.credential.proof.length);
CU_ASSERT(memcmp("PROOF", oframe.credential.proof.data, 5) == 0);
CU_ASSERT(2 == oframe.credential.ncerts);
CU_ASSERT(5 == oframe.credential.certs[0].length);
CU_ASSERT(memcmp("CERT0", oframe.credential.certs[0].data, 5) == 0);
CU_ASSERT(5 == oframe.credential.certs[1].length);
CU_ASSERT(memcmp("CERT1", oframe.credential.certs[1].data, 5) == 0);
CU_ASSERT(SPDYLAY_PROTO_SPDY3 == oframe.credential.hd.version);
CU_ASSERT(SPDYLAY_CREDENTIAL == oframe.credential.hd.type);
CU_ASSERT(SPDYLAY_CTRL_FLAG_NONE == oframe.credential.hd.flags);
CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.credential.hd.length);
spdylay_frame_credential_free(&oframe.credential);
/* Put large certificate length */
spdylay_put_uint32be(&buf[8+2+4+5], INT32_MAX);
CU_ASSERT(SPDYLAY_ERR_INVALID_FRAME == spdylay_frame_unpack_credential
(&oframe.credential,
&buf[0], SPDYLAY_FRAME_HEAD_LENGTH,
&buf[SPDYLAY_FRAME_HEAD_LENGTH],
framelen-SPDYLAY_FRAME_HEAD_LENGTH));
free(buf);
spdylay_frame_credential_free(&frame.credential);
}
void test_spdylay_frame_nv_sort(void)
{
char *nv[7];
@ -670,3 +730,49 @@ void test_spdylay_frame_unpack_nv_check_name_spdy3(void)
test_spdylay_frame_unpack_nv_check_name_with
(spdylay_frame_get_len_size(SPDYLAY_PROTO_SPDY3));
}
void test_spdylay_frame_nv_set_origin(void)
{
spdylay_origin origin;
const char *nv1[] = {
":host", "example.org",
":scheme", "https",
NULL
};
const char *nv2[] = {
":host", "example.org:8443",
":scheme", "https",
NULL
};
const char *nv3[] = {
":host", "example.org:0",
":scheme", "https",
NULL
};
const char *nv4[] = {
":host", "example.org",
NULL
};
const char *nv5[] = {
":scheme", "https",
NULL
};
CU_ASSERT(0 == spdylay_frame_nv_set_origin((char**)nv1, &origin));
CU_ASSERT(strcmp("https", origin.scheme) == 0);
CU_ASSERT(strcmp("example.org", origin.host) == 0);
CU_ASSERT(443 == origin.port);
CU_ASSERT(0 == spdylay_frame_nv_set_origin((char**)nv2, &origin));
CU_ASSERT(strcmp("https", origin.scheme) == 0);
CU_ASSERT(strcmp("example.org", origin.host) == 0);
CU_ASSERT(8443 == origin.port);
CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==
spdylay_frame_nv_set_origin((char**)nv3, &origin));
CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==
spdylay_frame_nv_set_origin((char**)nv4, &origin));
CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==
spdylay_frame_nv_set_origin((char**)nv5, &origin));
}

View File

@ -42,11 +42,13 @@ void test_spdylay_frame_pack_headers_spdy3(void);
void test_spdylay_frame_pack_window_update(void);
void test_spdylay_frame_pack_settings_spdy2(void);
void test_spdylay_frame_pack_settings_spdy3(void);
void test_spdylay_frame_pack_credential(void);
void test_spdylay_frame_nv_sort(void);
void test_spdylay_frame_nv_downcase(void);
void test_spdylay_frame_nv_2to3(void);
void test_spdylay_frame_nv_3to2(void);
void test_spdylay_frame_unpack_nv_check_name_spdy2(void);
void test_spdylay_frame_unpack_nv_check_name_spdy3(void);
void test_spdylay_frame_nv_set_origin(void);
#endif /* SPDYLAY_FRAME_TEST_H */

View File

@ -1924,7 +1924,7 @@ void test_spdylay_session_on_settings_received(void)
iv[2].value = 64*1024;
iv[2].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;
iv[3].settings_id = SPDYLAY_SETTINGS_CURRENT_CWND;
iv[3].settings_id = SPDYLAY_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE;
iv[3].value = 512;
iv[3].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;
@ -1933,7 +1933,7 @@ void test_spdylay_session_on_settings_received(void)
iv[4].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;
memset(&callbacks, 0, sizeof(spdylay_session_callbacks));
spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks,
spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks,
&user_data);
session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE] = 16*1024;
@ -1953,8 +1953,11 @@ void test_spdylay_session_on_settings_received(void)
session->remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS]);
CU_ASSERT(64*1024 ==
session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]);
CU_ASSERT(512 ==
session->remote_settings[SPDYLAY_SETTINGS_CURRENT_CWND]);
/* We limit certificate vector in reasonable size. */
CU_ASSERT(SPDYLAY_MAX_CLIENT_CERT_VECTOR_LENGTH ==
session->remote_settings
[SPDYLAY_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE]);
CU_ASSERT(SPDYLAY_MAX_CLIENT_CERT_VECTOR_LENGTH == session->cli_certvec.size);
CU_ASSERT(64*1024 == stream1->window_size);
CU_ASSERT(0 == stream2->window_size);
@ -2064,3 +2067,157 @@ void test_spdylay_session_get_outbound_queue_size(void)
spdylay_session_del(session);
}
static ssize_t get_credential_ncerts(spdylay_session *session,
const spdylay_origin *origin,
void *user_data)
{
if(strcmp("example.org", origin->host) == 0 &&
strcmp("https", origin->scheme) == 0 &&
443 == origin->port) {
return 2;
} else {
return 0;
}
}
static ssize_t get_credential_cert(spdylay_session *session,
const spdylay_origin *origin,
size_t index,
uint8_t *cert, size_t certlen,
void *user_data)
{
size_t len = strlen(origin->host);
if(certlen == 0) {
return len;
} else {
assert(certlen == len);
memcpy(cert, origin->host, len);
return 0;
}
}
static ssize_t get_credential_proof(spdylay_session *session,
const spdylay_origin *origin,
uint8_t *proof, size_t prooflen,
void *uer_data)
{
size_t len = strlen(origin->scheme);
if(prooflen == 0) {
return len;
} else {
assert(prooflen == len);
memcpy(proof, origin->scheme, len);
return 0;
}
}
void test_spdylay_session_prep_credential(void)
{
spdylay_session *session;
spdylay_session_callbacks callbacks;
const char *nv[] = { ":host", "example.org",
":scheme", "https",
NULL };
const char *nv_nocert[] = { ":host", "nocert",
":scheme", "https",
NULL };
spdylay_frame frame, *cred_frame;
spdylay_outbound_item *item;
size_t i;
memset(&callbacks, 0, sizeof(spdylay_session_callbacks));
callbacks.get_credential_ncerts = get_credential_ncerts;
callbacks.get_credential_cert = get_credential_cert;
callbacks.get_credential_proof = get_credential_proof;
CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3,
&callbacks, NULL));
spdylay_frame_syn_stream_init(&frame.syn_stream, session->version,
SPDYLAY_CTRL_FLAG_NONE, 1, 0, 0,
dup_nv(nv));
CU_ASSERT(SPDYLAY_ERR_CREDENTIAL_PENDING ==
spdylay_session_prep_credential(session, &frame.syn_stream));
item = spdylay_session_get_next_ob_item(session);
CU_ASSERT(SPDYLAY_CREDENTIAL == OB_CTRL_TYPE(item));
CU_ASSERT(SPDYLAY_OB_PRI_CREDENTIAL == item->pri);
cred_frame = OB_CTRL(item);
CU_ASSERT(strlen("https") == cred_frame->credential.proof.length);
CU_ASSERT(memcmp("https", cred_frame->credential.proof.data,
cred_frame->credential.proof.length) == 0);
CU_ASSERT(2 == cred_frame->credential.ncerts);
for(i = 0; i < cred_frame->credential.ncerts; ++i) {
CU_ASSERT(strlen("example.org") == cred_frame->credential.certs[i].length);
CU_ASSERT(memcmp("example.org", cred_frame->credential.certs[i].data,
cred_frame->credential.certs[i].length) == 0);
}
/* Next spdylay_session_get_next_ob_item() call returns slot index */
CU_ASSERT(1 == spdylay_session_prep_credential(session, &frame.syn_stream));
spdylay_frame_syn_stream_free(&frame.syn_stream);
spdylay_frame_syn_stream_init(&frame.syn_stream, session->version,
SPDYLAY_CTRL_FLAG_NONE, 1, 0, 0,
dup_nv(nv_nocert));
CU_ASSERT(0 == spdylay_session_prep_credential(session, &frame.syn_stream));
spdylay_frame_syn_stream_free(&frame.syn_stream);
spdylay_session_del(session);
}
void test_spdylay_submit_syn_stream_with_credential(void)
{
spdylay_session *session;
spdylay_session_callbacks callbacks;
const char *nv[] = { ":host", "example.org",
":scheme", "https",
NULL };
my_user_data ud;
accumulator acc;
ud.acc = &acc;
memset(&callbacks, 0, sizeof(spdylay_session_callbacks));
callbacks.send_callback = block_count_send_callback;
callbacks.on_ctrl_send_callback = on_ctrl_send_callback;
callbacks.get_credential_ncerts = get_credential_ncerts;
callbacks.get_credential_cert = get_credential_cert;
callbacks.get_credential_proof = get_credential_proof;
CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3,
&callbacks, &ud));
CU_ASSERT(0 == spdylay_submit_request(session, 0, nv, NULL, NULL));
ud.block_count = 1;
ud.ctrl_send_cb_called = 0;
CU_ASSERT(0 == spdylay_session_send(session));
CU_ASSERT(1 == ud.ctrl_send_cb_called);
CU_ASSERT(SPDYLAY_CREDENTIAL == ud.sent_frame_type);
session->callbacks.send_callback = accumulator_send_callback;
acc.length = 0;
ud.ctrl_send_cb_called = 0;
CU_ASSERT(0 == spdylay_session_send(session));
CU_ASSERT(1 == ud.ctrl_send_cb_called);
CU_ASSERT(SPDYLAY_SYN_STREAM == ud.sent_frame_type);
/* Check slot */
CU_ASSERT(1 == acc.buf[17]);
spdylay_session_del(session);
}
void test_spdylay_session_set_initial_client_cert_origin(void)
{
spdylay_session *session;
spdylay_session_callbacks callbacks;
const spdylay_origin *origin;
spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks, NULL);
CU_ASSERT(0 == spdylay_session_set_initial_client_cert_origin
(session, "https", "example.org", 443));
origin = spdylay_session_get_client_cert_origin(session, 1);
CU_ASSERT(NULL != origin);
CU_ASSERT(strcmp("https", spdylay_origin_get_scheme(origin)) == 0);
CU_ASSERT(strcmp("example.org", spdylay_origin_get_host(origin)) == 0);
CU_ASSERT(443 == spdylay_origin_get_port(origin));
spdylay_session_del(session);
}

View File

@ -64,5 +64,8 @@ void test_spdylay_session_on_ctrl_not_send(void);
void test_spdylay_session_on_settings_received(void);
void test_spdylay_submit_settings(void);
void test_spdylay_session_get_outbound_queue_size(void);
void test_spdylay_session_prep_credential(void);
void test_spdylay_submit_syn_stream_with_credential(void);
void test_spdylay_session_set_initial_client_cert_origin(void);
#endif /* SPDYLAY_SESSION_TEST_H */