diff options
author | Rob Austein <sra@hactrn.net> | 2013-08-24 19:44:12 +0000 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2013-08-24 19:44:12 +0000 |
commit | a03d6db3f048680f59db2a49f980150489674042 (patch) | |
tree | 74d4efdd0ef0b6dba01ffe7eb8191b84cc0d72c1 /rpkid | |
parent | ef66b606d993faa8ab56e979fd59bf18ee820ca6 (diff) |
Basic certificate verification works. Needs a bunch of additional
methods, properties, and constants to be fully useful.
svn path=/trunk/; revision=5463
Diffstat (limited to 'rpkid')
-rw-r--r-- | rpkid/ext/POW.c | 304 |
1 files changed, 221 insertions, 83 deletions
diff --git a/rpkid/ext/POW.c b/rpkid/ext/POW.c index 4546b1c4..141cedb0 100644 --- a/rpkid/ext/POW.c +++ b/rpkid/ext/POW.c @@ -266,6 +266,13 @@ static PyObject static PyObject *custom_datetime; /* + * "ex_data" index for pointer we want to attach to X509_STORE_CTX so + * we can extract it in callbacks. + */ + +static int x509_store_ctx_ex_data_idx = -1; + +/* * Declarations of type objects (definitions come later). */ @@ -304,6 +311,13 @@ typedef struct { typedef struct { PyObject_HEAD + X509_STORE_CTX *ctx; + PyObject *cb; + int initialized; +} x509_store_ctx_object; + +typedef struct { + PyObject_HEAD X509_CRL *crl; } crl_object; @@ -339,20 +353,6 @@ typedef struct { STACK_OF(X509_EXTENSION) *exts; } pkcs10_object; -/* - * Wrapper for OpenSSL's X509_STORE_CTX, so that we can pass a Python - * callback method through to the C callback function. X509_STORE_CTX - * *must* be the first element of this structure, so that the ugly - * (but safe, according to the C language definition) cast will work - * to convert a pointer to the X509_STORE_CTX back to a pointer to our - * larger structure. - */ - -typedef struct { - X509_STORE_CTX ctx; /* Must be first */ - PyObject *cb; /* Python callback */ -} X509_STORE_CTX_with_Python_callback; - /* @@ -3559,8 +3559,126 @@ x509_store_object_add_crl(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" +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), + {NULL} +}; + +static char POW_X509Store_Type__doc__[] = + "This class holds the OpenSSL certificate store objects used in CMS\n" + "and certificate verification.\n" + "\n" + LAME_DISCLAIMER_IN_ALL_CLASS_DOCUMENTATION + ; + +static PyTypeObject POW_X509Store_Type = { + PyObject_HEAD_INIT(0) + 0, /* ob_size */ + "rpki.POW.X509Store", /* tp_name */ + sizeof(x509_store_object), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)x509_store_object_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + POW_X509Store_Type__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + x509_store_object_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + x509_store_object_new, /* tp_new */ +}; + + + +/* + * X509StoreCTX object. + */ + +static PyObject * +x509_store_ctx_object_new(PyTypeObject *type, GCC_UNUSED PyObject *args, GCC_UNUSED PyObject *kwds) +{ + x509_store_ctx_object *self = NULL; + + ENTERING(x509_store_ctx_object_new); + + if ((self = (x509_store_ctx_object *) type->tp_alloc(type, 0)) == NULL) + goto error; + + if ((self->ctx = X509_STORE_CTX_new()) == NULL) + lose_no_memory(); + + self->initialized = 0; + self->cb = Py_None; + Py_XINCREF(self->cb); + return (PyObject *) self; + + error: + Py_XDECREF(self); + return NULL; +} + +static int +x509_store_ctx_object_init(x509_store_ctx_object *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"store", NULL}; + x509_store_object *store = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!", kwlist, &POW_X509Store_Type, &store)) + goto error; + + if (!X509_STORE_CTX_init(self->ctx, store ? store->store : NULL, NULL, NULL)) + lose_openssl_error("Couldn't initialize X509_STORE_CTX"); + + self->initialized = 1; + + if (!X509_STORE_CTX_set_ex_data(self->ctx, x509_store_ctx_ex_data_idx, self)) + lose_openssl_error("Couldn't set X509_STORE_CTX ex_data"); + + return 0; + + error: + return -1; +} + +static void +x509_store_ctx_object_dealloc(x509_store_ctx_object *self) +{ + ENTERING(x509_store_ctx_object_dealloc); + Py_XDECREF(self->cb); + if (self->initialized) + X509_STORE_CTX_cleanup(self->ctx); + X509_STORE_CTX_free(self->ctx); + self->ob_type->tp_free((PyObject*) self); +} + +static char x509_store_ctx_object_verify__doc__[] = + "Verify an X509 certificate object using this certificate store context.\n" "\n" "The \"certificate\" parameter is the certificate to verify, and\n" "should be an X509 object.\n" @@ -3569,50 +3687,30 @@ static char x509_store_object_verify__doc__[] = "will be added to the set of untrusted certificates from which OpenSSL\n" "will attempt to build a chain to a trusted certificate.\n" "\n" - "The \"callback\" parameter is a callable Python object which takes two\n" - "arguments, the \"ok\" value from the OpenSSL callback, and the code\n" - "returned by X509_STORE_CTX_get_error(ctx); both of these are integers.\n" - "This interface may be replaced at some point in the future by something\n" - "that exposes more of the X509_STORE_CTX state.\n" + "The \"callback\" parameter is a callable Python object which receives two\n" + "arguments, the integer \"ok\" value from the OpenSSL callback, and the\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" "\n" "The \"crl_check\", \"crl_check_all\", and \"ignore_critical\" arguments\n" "are boolean flags corresponding to X509_V_FLAG_CRL_CHECK,\n" "X509_V_FLAG_CRL_CHECK_ALL, and X509_V_FLAG_IGNORE_CRITICAL\",\n" "respectively.\n" "\n" - "The return value is currently a 3-element tuple consisting of:\n" - "\n" - " * The numeric return value from X509_verify_cert()\n" - " * The numeric error code value from the X509_STORE_CTX\n" - " * The numeric error_depth value from the X509_STORE_CTX\n" - "\n" - "Other values may added to this tuple later, if needed.\n" + "This method returns the numeric return value from X509_verify_cert().\n" ; -/* - * - * I suspect that the right thing here would be to make a Python - * object containing X509_STORE_CTX, initialize it in - * x509_store_object_verify(), pass it as the second argument to the - * callback, and return it along with X509_verify_cert()'s numeric - * return value as the results from x509_store_object_verify(). But - * that would be a lot of work, and we may not really need all that. - * - * Also, we might want to support X509_V_FLAG_USE_CHECK_TIME and - * X509_V_FLAG_EXPLICIT_POLICY, but let's stick to simple stuff until - * we have the Python callback code working. - */ - static int -x509_store_object_verify_cb(int ok, X509_STORE_CTX *ctx) +x509_store_ctx_object_verify_cb(int ok, X509_STORE_CTX *ctx) { - X509_STORE_CTX_with_Python_callback *pctx = (X509_STORE_CTX_with_Python_callback *) ctx; - int code = X509_STORE_CTX_get_error(ctx); + x509_store_ctx_object *self = X509_STORE_CTX_get_ex_data(ctx, x509_store_ctx_ex_data_idx); PyObject *arglist = NULL; PyObject *result = NULL; - arglist = Py_BuildValue("(ii)", ok, code); - result = PyObject_CallObject(pctx->cb, arglist); + arglist = Py_BuildValue("(iO)", ok, self); + result = PyObject_CallObject(self->cb, arglist); ok = result == NULL ? -1 : PyObject_IsTrue(result); @@ -3621,9 +3719,8 @@ x509_store_object_verify_cb(int ok, X509_STORE_CTX *ctx) return ok; } - static PyObject * -x509_store_object_verify(x509_store_object *self, PyObject *args, PyObject *kwds) +x509_store_ctx_object_verify(x509_store_ctx_object *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"certificate", "untrusted", "callback", "crl_check", "crl_check_all", "ignore_critical", NULL}; @@ -3632,12 +3729,12 @@ x509_store_object_verify(x509_store_object *self, PyObject *args, PyObject *kwds PyObject *flag_CRL_CHECK = Py_False; PyObject *flag_CRL_CHECK_ALL = Py_False; PyObject *flag_IGNORE_CRITICAL = Py_False; - X509_STORE_CTX_with_Python_callback pctx; x509_object *x509 = NULL; STACK_OF(X509) *x509_stack = NULL; + PyObject *old_callback = NULL; PyObject *result = NULL; - int ok, initialized = 0; unsigned long flags; + int ok; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|OOOOO", kwlist, &POW_X509_Type, &x509, &x509_sequence, &callback, @@ -3658,54 +3755,91 @@ x509_store_object_verify(x509_store_object *self, PyObject *args, PyObject *kwds if (PyObject_IsTrue(flag_IGNORE_CRITICAL)) flags |= X509_V_FLAG_IGNORE_CRITICAL; - X509_STORE_CTX_init(&pctx.ctx, self->store, x509->x509, x509_stack); - pctx.cb = callback; - Py_XINCREF(pctx.cb); - initialized = 1; + X509_STORE_CTX_set_cert(self->ctx, x509->x509); - X509_VERIFY_PARAM_set_flags(pctx.ctx.param, flags); + if (x509_stack) + X509_STORE_CTX_set_chain(self->ctx, x509_stack); - if (pctx.cb != Py_None) - X509_STORE_CTX_set_verify_cb(&pctx.ctx, x509_store_object_verify_cb); + X509_VERIFY_PARAM_set_flags(self->ctx->param, flags); - ok = X509_verify_cert(&pctx.ctx) == 1; + if (callback != Py_None) { + old_callback = self->cb; + self->cb = callback; + Py_XINCREF(callback); + } - /* - * I don't like this return convention very much, but let's get - * callbacks working before messing with this. - */ - result = Py_BuildValue("(iii)", ok, pctx.ctx.error, pctx.ctx.error_depth); + if (self->cb != Py_None) + X509_STORE_CTX_set_verify_cb(self->ctx, x509_store_ctx_object_verify_cb); + + ok = X509_verify_cert(self->ctx); + + if (callback != Py_None) { + self->cb = old_callback; + Py_XDECREF(callback); + } + + if (ok < 0) + lose_openssl_error("X509_verify_cert() returned exception"); + + result = Py_BuildValue("i", ok); error: /* fall through */ sk_X509_free(x509_stack); - if (initialized) { - X509_STORE_CTX_cleanup(&pctx.ctx); - Py_XDECREF(pctx.cb); - } return result; } -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(verify, x509_store_object_verify, METH_KEYWORDS), +static char x509_store_ctx_object_get_error__doc__[] = + "Extract verification error code from this X509StoreCTX.\n" + ; + +static PyObject* +x509_store_ctx_object_get_error (x509_store_ctx_object *self) +{ + return Py_BuildValue("i", X509_STORE_CTX_get_error(self->ctx)); +} + +static char x509_store_ctx_object_get_error_depth__doc__[] = + "Extract verification error depth from this X509StoreCTX.\n" + ; + +static PyObject* +x509_store_ctx_object_get_error_depth (x509_store_ctx_object *self) +{ + return Py_BuildValue("i", X509_STORE_CTX_get_error_depth(self->ctx)); +} + +/* + * See (omnibus) man page for X509_STORE_CTX_get_error() for other + * query methods we might want to expose. + */ + +/* + * We might want to support X509_V_FLAG_USE_CHECK_TIME and + * X509_V_FLAG_EXPLICIT_POLICY, but let's stick to simple stuff until + * we have the Python callback code working. + */ + +static struct PyMethodDef x509_store_ctx_object_methods[] = { + Define_Method(verify, x509_store_ctx_object_verify, METH_KEYWORDS), + Define_Method(getError, x509_store_ctx_object_get_error, METH_NOARGS), + Define_Method(getErrorDepth, x509_store_ctx_object_get_error_depth, METH_NOARGS), {NULL} }; -static char POW_X509Store_Type__doc__[] = - "This class holds the OpenSSL certificate store objects used in CMS\n" - "verification.\n" +static char POW_X509StoreCTX_Type__doc__[] = + "This class holds the OpenSSL certificate store context objects used\n" + "in certificate verification.\n" "\n" LAME_DISCLAIMER_IN_ALL_CLASS_DOCUMENTATION ; -static PyTypeObject POW_X509Store_Type = { +static PyTypeObject POW_X509StoreCTX_Type = { PyObject_HEAD_INIT(0) 0, /* ob_size */ - "rpki.POW.X509Store", /* tp_name */ - sizeof(x509_store_object), /* tp_basicsize */ + "rpki.POW.X509StoreCTX", /* tp_name */ + sizeof(x509_store_ctx_object), /* tp_basicsize */ 0, /* tp_itemsize */ - (destructor)x509_store_object_dealloc, /* tp_dealloc */ + (destructor)x509_store_ctx_object_dealloc,/* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -3721,14 +3855,14 @@ static PyTypeObject POW_X509Store_Type = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - POW_X509Store_Type__doc__, /* tp_doc */ + POW_X509StoreCTX_Type__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - x509_store_object_methods, /* tp_methods */ + x509_store_ctx_object_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ @@ -3736,9 +3870,9 @@ static PyTypeObject POW_X509Store_Type = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - 0, /* tp_init */ + (initproc) x509_store_ctx_object_init, /* tp_init */ 0, /* tp_alloc */ - x509_store_object_new, /* tp_new */ + x509_store_ctx_object_new, /* tp_new */ }; @@ -8406,6 +8540,7 @@ init_POW(void) Define_Class(POW_X509_Type); Define_Class(POW_X509Store_Type); + Define_Class(POW_X509StoreCTX_Type); Define_Class(POW_CRL_Type); Define_Class(POW_Asymmetric_Type); Define_Class(POW_Digest_Type); @@ -8475,6 +8610,9 @@ init_POW(void) OpenSSL_ok &= create_missing_nids(); + x509_store_ctx_ex_data_idx = X509_STORE_CTX_get_ex_new_index(0, "x590_store_ctx_object for verify callback", + NULL, NULL, NULL); + if (PyErr_Occurred() || !OpenSSL_ok) Py_FatalError("Can't initialize module POW"); } |