aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2013-08-28 00:16:00 +0000
committerRob Austein <sra@hactrn.net>2013-08-28 00:16:00 +0000
commit4bedd1c6226b5391eb4d7f400ca8adf60156f830 (patch)
treefc06dc0dc50fa01e7dc21e13d1c933bc028849d4
parent0c23203c72fc37c4fe3431ed6040ad0c50bf0945 (diff)
Rework the new certificate validation code to better encapsulate the
underlying semantics in a Pythonic wrapper: X509Store.verify() now instantiates X509StoreCTX, and .verify_callback() is now an optional method supplied by the user via subclassing. svn path=/trunk/; revision=5471
-rw-r--r--rpkid/ext/POW.c343
1 files changed, 200 insertions, 143 deletions
diff --git a/rpkid/ext/POW.c b/rpkid/ext/POW.c
index 92a3af9e..fdc849f7 100644
--- a/rpkid/ext/POW.c
+++ b/rpkid/ext/POW.c
@@ -149,6 +149,7 @@ define GCC_UNUSED
/* Object check functions */
#define POW_X509_Check(op) PyObject_TypeCheck(op, &POW_X509_Type)
#define POW_X509Store_Check(op) PyObject_TypeCheck(op, &POW_X509Store_Type)
+#define POW_X509StoreCTX_Check(op) PyObject_TypeCheck(op, &POW_X509StoreCTX_Type)
#define POW_CRL_Check(op) PyObject_TypeCheck(op, &POW_CRL_Type)
#define POW_Asymmetric_Check(op) PyObject_TypeCheck(op, &POW_Asymmetric_Type)
#define POW_Digest_Check(op) PyObject_TypeCheck(op, &POW_Digest_Type)
@@ -279,6 +280,7 @@ static int x509_store_ctx_ex_data_idx = -1;
static PyTypeObject
POW_X509_Type,
POW_X509Store_Type,
+ POW_X509StoreCTX_Type,
POW_CRL_Type,
POW_Asymmetric_Type,
POW_Digest_Type,
@@ -307,7 +309,7 @@ typedef struct {
typedef struct {
PyObject_HEAD
X509_STORE *store;
- PyObject *cb;
+ PyObject *ctxclass;
} x509_store_object;
typedef struct {
@@ -694,6 +696,9 @@ x509_helper_iterable_to_stack(PyObject *iterable)
* in any case.
*/
+static x509_object *x509_object_new_helper(PyTypeObject *, X509 *);
+static crl_object *crl_object_new_helper (PyTypeObject *, X509_CRL *);
+
static PyObject *
stack_to_tuple_helper(_STACK *sk, PyObject *(*handler)(void *))
{
@@ -721,6 +726,32 @@ stack_to_tuple_helper(_STACK *sk, PyObject *(*handler)(void *))
return NULL;
}
+static PyObject *
+stack_to_tuple_helper_get_x509(void *cert)
+{
+ x509_object *obj;
+
+ ENTERING(stack_to_tuple_helper_get_x509);
+
+ if ((obj = x509_object_new_helper(NULL, cert)) == NULL)
+ return NULL;
+
+ return (PyObject *) obj;
+}
+
+static PyObject *
+stack_to_tuple_helper_get_crl(void *crl)
+{
+ crl_object *obj;
+
+ ENTERING(stack_to_tuple_helper_get_crl);
+
+ if ((obj = crl_object_new_helper(NULL, crl)) == NULL)
+ return NULL;
+
+ return (PyObject *) obj;
+}
+
/*
* Time conversion functions. Obvious mapping into Python data types
* is datetime, or, rather, our customized rpki.sundial.datetime.
@@ -1502,18 +1533,39 @@ static PyTypeObject POW_IPAddress_Type = {
* X509 object.
*/
+static x509_object *
+x509_object_new_helper(PyTypeObject *type, X509 *x)
+{
+ x509_object *self;
+
+ if (type == NULL)
+ type = &POW_X509_Type;
+
+ if ((self = (x509_object *) type->tp_alloc(type, 0)) == NULL)
+ return NULL;
+
+ self->x509 = x;
+ return self;
+}
+
static PyObject *
x509_object_new(PyTypeObject *type, GCC_UNUSED PyObject *args, GCC_UNUSED PyObject *kwds)
{
- x509_object *self;
+ x509_object *self = NULL;
+ X509 *x = NULL;
ENTERING(x509_object_new);
- if ((self = (x509_object *) type->tp_alloc(type, 0)) != NULL &&
- (self->x509 = X509_new()) != NULL)
- return (PyObject *) self;
+ if ((x = X509_new()) == NULL)
+ lose_no_memory();
- Py_XDECREF(self);
+ if ((self = x509_object_new_helper(type, x)) == NULL)
+ goto error;
+
+ return (PyObject *) self;
+
+ error:
+ X509_free(x);
return NULL;
}
@@ -3494,8 +3546,8 @@ x509_store_object_new(PyTypeObject *type, GCC_UNUSED PyObject *args, GCC_UNUSED
if ((self->store = X509_STORE_new()) == NULL)
lose_no_memory();
- self->cb = Py_None;
- Py_XINCREF(self->cb);
+ self->ctxclass = (PyObject *) &POW_X509StoreCTX_Type;
+ Py_XINCREF(self->ctxclass);
return (PyObject *) self;
error:
@@ -3508,7 +3560,7 @@ x509_store_object_dealloc(x509_store_object *self)
{
ENTERING(x509_store_object_dealloc);
X509_STORE_free(self->store);
- Py_XDECREF(self->cb);
+ Py_XDECREF(self->ctxclass);
self->ob_type->tp_free((PyObject*) self);
}
@@ -3562,31 +3614,22 @@ x509_store_object_add_crl(x509_store_object *self, PyObject *args)
return NULL;
}
-static char x509_store_object_set_callback__doc__[] =
- "Set validation callback for this X509Store.\n"
+static char x509_store_object_set_flags__doc__[] =
+ "Set validation flags for this X509Store.\n"
"\n"
- "The callback is a callable Python object which receives two\n"
- "arguments, the integer \"ok\" value from the OpenSSL callback, and a\n"
- "X509StoreCTX. The return value from the callback is interpreted as a\n"
- "boolean value: anything which evaluates to True will be interpreted as\n"
- "allowing whatever the callback reported, while anything evaluating to False\n"
- "will be interpreted as disallowing whatever the callback reported.\n"
+ "Argument is an integer containing bit flags to set.\n"
;
static PyObject *
-x509_store_object_set_callback (x509_store_object *self, PyObject *args)
+x509_store_object_set_flags (x509_store_object *self, PyObject *args)
{
- PyObject *cb = Py_None;
+ unsigned long flags;
- if (!PyArg_ParseTuple(args, "O", &cb))
+ if (!PyArg_ParseTuple(args, "k", &flags))
goto error;
- if (cb != Py_None && !PyCallable_Check(cb))
- lose("Object is not callable");
-
- Py_XDECREF(self->cb);
- self->cb = cb == Py_None ? NULL : cb;
- Py_XINCREF(self->cb);
+ if (!X509_VERIFY_PARAM_set_flags(self->store->param, flags))
+ lose_openssl_error("X509_VERIFY_PARAM_set_flags() failed");
Py_RETURN_NONE;
@@ -3594,22 +3637,22 @@ x509_store_object_set_callback (x509_store_object *self, PyObject *args)
return NULL;
}
-static char x509_store_object_set_flags__doc__[] =
- "Set validation flags for this X509Store.\n"
+static char x509_store_object_clear_flags__doc__[] =
+ "Clear validation flags for this X509Store.\n"
"\n"
- "Argument is an integer containing bit flags to set.\n"
+ "Argument is an integer containing bit flags to clear.\n"
;
static PyObject *
-x509_store_object_set_flags (x509_store_object *self, PyObject *args)
+x509_store_object_clear_flags (x509_store_object *self, PyObject *args)
{
unsigned long flags;
if (!PyArg_ParseTuple(args, "k", &flags))
goto error;
- if (!X509_VERIFY_PARAM_set_flags(self->store->param, flags))
- lose_openssl_error("X509_VERIFY_PARAM_set_flags() failed");
+ if (!X509_VERIFY_PARAM_clear_flags(self->store->param, flags))
+ lose_openssl_error("X509_VERIFY_PARAM_clear_flags() failed");
Py_RETURN_NONE;
@@ -3617,22 +3660,29 @@ x509_store_object_set_flags (x509_store_object *self, PyObject *args)
return NULL;
}
-static char x509_store_object_clear_flags__doc__[] =
- "Clear validation flags for this X509Store.\n"
+static char x509_store_object_set_context_class__doc__[] =
+ "Set validation context class factory for this X509Store.\n"
"\n"
- "Argument is an integer containing bit flags to clear.\n"
+ "This must be a callable object which takes one argument, an X509Store,\n"
+ "and returns a subclass of X509StoreCTX. The callable can be a class\n"
+ "object but need not be, so long as calling it returns an instance of an\n"
+ "appropriate class. The default is X509StoreCTX.\n"
;
static PyObject *
-x509_store_object_clear_flags (x509_store_object *self, PyObject *args)
+x509_store_object_set_context_class (x509_store_object *self, PyObject *args)
{
- unsigned long flags;
+ PyObject *ctxclass = (PyObject *) &POW_X509StoreCTX_Type;
- if (!PyArg_ParseTuple(args, "k", &flags))
+ if (!PyArg_ParseTuple(args, "|O", &ctxclass))
goto error;
- if (!X509_VERIFY_PARAM_clear_flags(self->store->param, flags))
- lose_openssl_error("X509_VERIFY_PARAM_clear_flags() failed");
+ if (!PyCallable_Check(ctxclass))
+ lose("Context class must be callable");
+
+ Py_XDECREF(self->ctxclass);
+ self->ctxclass = ctxclass;
+ Py_XINCREF(self->ctxclass);
Py_RETURN_NONE;
@@ -3640,12 +3690,73 @@ x509_store_object_clear_flags (x509_store_object *self, PyObject *args)
return NULL;
}
+static char x509_store_object_verify__doc__[] =
+ "Verify an X509 certificate object using this certificate store.\n"
+ "\n"
+ "Optional second argument is an iterable that supplies untrusted certificates\n"
+ "to be considered when building a chain to the trust anchor.\n"
+ "\n"
+ "This method returns an instance of the store's verification context class.\n"
+ ;
+
+static PyObject *
+x509_store_object_verify(x509_store_object *self, PyObject *args)
+{
+ x509_store_ctx_object *ctx = NULL;
+ STACK_OF(X509) *stack = NULL;
+ x509_object *x509 = NULL;
+ PyObject *chain = Py_None;
+ int ok;
+
+ if (!PyArg_ParseTuple(args, "O!|O", &POW_X509_Type, &x509, &chain))
+ goto error;
+
+ if ((ctx = (x509_store_ctx_object *) PyObject_CallFunctionObjArgs(self->ctxclass, self, NULL)) == NULL)
+ goto error;
+
+ if (!POW_X509StoreCTX_Check(ctx))
+ lose_type_error("Returned context is not a X509StoreCTX");
+
+ if (ctx->ctx == NULL)
+ lose("Uninitialized X509StoreCTX");
+
+ if (chain != Py_None && (stack = x509_helper_iterable_to_stack(chain)) == NULL)
+ goto error;
+
+ Py_XINCREF(x509);
+ Py_XINCREF(chain);
+ X509_STORE_CTX_set_cert(ctx->ctx, x509->x509);
+ X509_STORE_CTX_set_chain(ctx->ctx, stack);
+
+ ok = X509_verify_cert(ctx->ctx);
+
+ X509_STORE_CTX_set_chain(ctx->ctx, NULL);
+ X509_STORE_CTX_set_cert(ctx->ctx, NULL);
+ Py_XDECREF(chain);
+ Py_XDECREF(x509);
+
+ sk_X509_free(stack);
+
+ if (ok < 0 && PyErr_Occurred())
+ goto error;
+
+ if (ok < 0)
+ lose_openssl_error("X509_verify_cert() raised an exception");
+
+ return (PyObject *) ctx;
+
+ error: /* fall through */
+ Py_XDECREF(ctx);
+ return NULL;
+}
+
static struct PyMethodDef x509_store_object_methods[] = {
Define_Method(addTrust, x509_store_object_add_trust, METH_VARARGS),
Define_Method(addCrl, x509_store_object_add_crl, METH_VARARGS),
- Define_Method(setCallback, x509_store_object_set_callback, METH_VARARGS),
+ Define_Method(setContextClass,x509_store_object_set_context_class, METH_VARARGS),
Define_Method(setFlags, x509_store_object_set_flags, METH_VARARGS),
Define_Method(clearFlags, x509_store_object_clear_flags, METH_VARARGS),
+ Define_Method(verify, x509_store_object_verify, METH_VARARGS),
{NULL}
};
@@ -3707,19 +3818,17 @@ static PyTypeObject POW_X509Store_Type = {
static int
x509_store_ctx_object_verify_cb(int ok, X509_STORE_CTX *ctx)
{
- x509_store_ctx_object *self = X509_STORE_CTX_get_ex_data(ctx, x509_store_ctx_ex_data_idx);
- PyObject *arglist = NULL;
+ static char method_name[] = "verify_callback";
+ PyObject *self = X509_STORE_CTX_get_ex_data(ctx, x509_store_ctx_ex_data_idx);
PyObject *result = NULL;
- if (self == NULL || self->store == NULL || self->store->cb == NULL)
+ if (self == NULL || !PyObject_HasAttrString(self, method_name))
return ok;
- arglist = Py_BuildValue("(iO)", ok, self);
- result = PyObject_CallObject(self->store->cb, arglist);
+ if ((result = PyObject_CallMethod(self, method_name, "i", ok)) == NULL)
+ return -1;
ok = result == NULL ? -1 : PyObject_IsTrue(result);
-
- Py_XDECREF(arglist);
Py_XDECREF(result);
return ok;
}
@@ -3787,52 +3896,6 @@ x509_store_ctx_object_get_store (x509_store_ctx_object *self, GCC_UNUSED void *c
return Py_BuildValue("O", self->store == NULL ? Py_None : (PyObject *) self->store);
}
-static char x509_store_ctx_object_verify__doc__[] =
- "Verify an X509 certificate object using this certificate store context.\n"
- "\n"
- "Optional second argument is an iterable that supplies untrusted certificates\n"
- "to be considered when building a chain to the trust anchor.\n"
- "\n"
- "This method returns the numeric return value from X509_verify_cert().\n"
- ;
-
-static PyObject *
-x509_store_ctx_object_verify(x509_store_ctx_object *self, PyObject *args)
-{
- STACK_OF(X509) *stack = NULL;
- x509_object *x509 = NULL;
- PyObject *chain = Py_None;
- PyObject *result = NULL;
- int ok;
-
- if (!PyArg_ParseTuple(args, "O!|O", &POW_X509_Type, &x509, &chain))
- goto error;
-
- if (chain != Py_None && (stack = x509_helper_iterable_to_stack(chain)) == NULL)
- goto error;
-
- Py_XINCREF(x509);
- Py_XINCREF(chain);
- X509_STORE_CTX_set_cert(self->ctx, x509->x509);
- X509_STORE_CTX_set_chain(self->ctx, stack);
-
- ok = X509_verify_cert(self->ctx);
-
- X509_STORE_CTX_set_chain(self->ctx, NULL);
- X509_STORE_CTX_set_cert(self->ctx, NULL);
- Py_XDECREF(chain);
- Py_XDECREF(x509);
-
- if (ok < 0)
- lose_openssl_error("X509_verify_cert() returned exception");
-
- result = Py_BuildValue("i", ok);
-
- error: /* fall through */
- sk_X509_free(stack);
- return result;
-}
-
static char x509_store_ctx_object_get_error__doc__[] =
"Extract verification error code from this X509StoreCTX.\n"
;
@@ -3880,11 +3943,9 @@ x509_store_ctx_object_get_current_certificate (x509_store_ctx_object *self)
if ((x = X509_dup(x)) == NULL)
lose_no_memory();
- if ((obj = (x509_object *) x509_object_new(&POW_X509_Type, NULL, NULL)) == NULL)
+ if ((obj = x509_object_new_helper(NULL, x)) == NULL)
goto error;
- X509_free(obj->x509);
- obj->x509 = x;
return (PyObject *) obj;
error:
@@ -3893,11 +3954,6 @@ x509_store_ctx_object_get_current_certificate (x509_store_ctx_object *self)
return NULL;
}
-/*
- * This needs renaming and refactoring, but not today.
- */
-static PyObject *cms_object_helper_get_cert(void *cert);
-
static char x509_store_ctx_object_get_chain__doc__[] =
"Extract certificate chain from X509StoreCTX. If validation\n"
"completed succesfully, this is the complete validation chain;\n"
@@ -3914,7 +3970,7 @@ x509_store_ctx_object_get_chain (x509_store_ctx_object *self)
lose_openssl_error("X509_STORE_CTX_get1_chain() failed");
result = stack_to_tuple_helper(CHECKED_PTR_OF(STACK_OF(X509), chain),
- cms_object_helper_get_cert);
+ stack_to_tuple_helper_get_x509);
error: /* fall through */
sk_X509_pop_free(chain, X509_free);
@@ -3974,7 +4030,6 @@ x509_store_ctx_object_set_policy (x509_store_ctx_object *self, PyObject *args)
*/
static struct PyMethodDef x509_store_ctx_object_methods[] = {
- Define_Method(verify, x509_store_ctx_object_verify, METH_VARARGS),
Define_Method(getError, x509_store_ctx_object_get_error, METH_NOARGS),
Define_Method(getErrorString, x509_store_ctx_object_get_error_string, METH_NOARGS),
Define_Method(getErrorDepth, x509_store_ctx_object_get_error_depth, METH_NOARGS),
@@ -3993,10 +4048,21 @@ static PyGetSetDef x509_store_ctx_object_getsetters[] = {
};
static char POW_X509StoreCTX_Type__doc__[] =
- "This class holds the OpenSSL certificate store context objects used\n"
- "in certificate verification.\n"
+ "This class holds the state of an OpenSSL certificate verification\n"
+ "operation. Ordinarily, the user will never have cause to instantiate\n"
+ "this class directly, instead, an object of this class will be returned\n"
+ "by X509Store.verify().\n"
"\n"
- LAME_DISCLAIMER_IN_ALL_CLASS_DOCUMENTATION
+ "If you need to see OpenSSL's verification callbacks, you can do so\n"
+ "by subclassing X509StoreCTX and attaching your subclass to an X509Store\n"
+ "object using X509Store.setContextClass(). Your subclass should provide\n"
+ "a .verify_callback() method, wich should expect to receive one argument:\n"
+ "the integer \"ok\" value passed by OpenSSL's verification callbacks.\n"
+ "\n"
+ "The return value from your .verify_callback() method will be is interpreted\n"
+ "as a boolean value: anything which evaluates to True will be result in a\n"
+ "return value of 1 to OpenSSL, while anything which evaluates to False will\n"
+ "result in a return value of 0 to OpenSSL.\n"
;
static PyTypeObject POW_X509StoreCTX_Type = {
@@ -4047,18 +4113,39 @@ static PyTypeObject POW_X509StoreCTX_Type = {
* CRL object.
*/
+static crl_object *
+crl_object_new_helper(PyTypeObject *type, X509_CRL *crl)
+{
+ crl_object *self = NULL;
+
+ if (type == NULL)
+ type = &POW_CRL_Type;
+
+ if ((self = (crl_object *) type->tp_alloc(type, 0)) == NULL)
+ return NULL;
+
+ self->crl = crl;
+ return self;
+}
+
static PyObject *
crl_object_new(PyTypeObject *type, GCC_UNUSED PyObject *args, GCC_UNUSED PyObject *kwds)
{
crl_object *self = NULL;
+ X509_CRL *crl = NULL;
ENTERING(crl_object_new);
- if ((self = (crl_object *) type->tp_alloc(type, 0)) != NULL &&
- (self->crl = X509_CRL_new()) != NULL)
- return (PyObject *) self;
+ if ((crl = X509_CRL_new()) == NULL)
+ lose_no_memory();
- Py_XDECREF(self);
+ if ((self = crl_object_new_helper(type, crl)) == NULL)
+ goto error;
+
+ return (PyObject *) self;
+
+ error:
+ X509_CRL_free(crl);
return NULL;
}
@@ -6052,21 +6139,6 @@ cms_object_pprint(cms_object *self)
return result;
}
-static PyObject *
-cms_object_helper_get_cert(void *cert)
-{
- x509_object *obj;
-
- ENTERING(cms_object_helper_get_cert);
-
- if ((obj = (x509_object *) x509_object_new(&POW_X509_Type, NULL, NULL)) == NULL)
- return NULL;
-
- X509_free(obj->x509);
- obj->x509 = cert;
- return (PyObject *) obj;
-}
-
static char cms_object_certs__doc__[] =
"Return any certificates embedded in this CMS message, as a\n"
"tuple of X509 objects. This tuple will be empty if the message\n"
@@ -6083,7 +6155,7 @@ cms_object_certs(cms_object *self)
if ((certs = CMS_get1_certs(self->cms)) != NULL)
result = stack_to_tuple_helper(CHECKED_PTR_OF(STACK_OF(X509), certs),
- cms_object_helper_get_cert);
+ stack_to_tuple_helper_get_x509);
else if (!ERR_peek_error())
result = Py_BuildValue("()");
else
@@ -6094,21 +6166,6 @@ cms_object_certs(cms_object *self)
return result;
}
-static PyObject *
-cms_object_helper_get_crl(void *crl)
-{
- crl_object *obj;
-
- ENTERING(cms_object_helper_get_crl);
-
- if ((obj = (crl_object *) crl_object_new(&POW_CRL_Type, NULL, NULL)) == NULL)
- return NULL;
-
- X509_CRL_free(obj->crl);
- obj->crl = crl;
- return (PyObject *) obj;
-}
-
static char cms_object_crls__doc__[] =
"Return any CRLs embedded in this CMS message, as a tuple of\n"
"CRL objects. This tuple will be empty if the message contains no CRLs.\n"
@@ -6124,7 +6181,7 @@ cms_object_crls(cms_object *self)
if ((crls = CMS_get1_crls(self->cms)) != NULL)
result = stack_to_tuple_helper(CHECKED_PTR_OF(STACK_OF(X509_CRL), crls),
- cms_object_helper_get_crl);
+ stack_to_tuple_helper_get_crl);
else if (!ERR_peek_error())
result = Py_BuildValue("()");
else