diff options
author | Rob Austein <sra@hactrn.net> | 2015-11-05 06:23:41 +0000 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2015-11-05 06:23:41 +0000 |
commit | df194d06e65bffa6e5ee5d094fe9ed90adaf46c8 (patch) | |
tree | f0636122675388ed2437202a8d7d3b8eabb05c09 /ext/POW.c | |
parent | 83db6916f7bc00ca8783f705a71c0a3493bcc6bb (diff) |
Add StatusCode.__repr__(). Add glorp to pass status set object
through to X509 verification callback handler so it can record status
properly.
svn path=/branches/tk705/; revision=6159
Diffstat (limited to 'ext/POW.c')
-rw-r--r-- | ext/POW.c | 159 |
1 files changed, 123 insertions, 36 deletions
@@ -309,6 +309,7 @@ typedef struct { PyObject_HEAD X509_STORE_CTX *ctx; x509_store_object *store; + PyObject *status; } x509_store_ctx_object; typedef struct { @@ -1122,44 +1123,13 @@ whack_ec_key_to_namedCurve(EVP_PKEY *pkey) * How to handle CMS cases: keep common command parser etc, but add an argument * to that helper function which takes a function pointer to do the extended * checking for this particular flavor of CMS object. - * - * Hmm. enum is the right type for C code, but is it right for - * Python? Perhaps the Python view of this should be a set of - * strings? Except there are two strings for each code: the name of - * the code and the string translation of the code. Could stuff all - * that into a dict(), of course. There's some kind of proxy dict we - * can use to present a read-only dict to the user, that might be the - * right approach here. Python implementation is getting a bit - * complicated. - * - * Or the codes could be read-only first class objects in their own - * right, with properties for numeric, name, and human readable - * string. __int__() and __str__() methods. Python side doesn't - * really need the numeric value if we go this way but including it is - * harmless. For that matter, we could define all of this in a Python - * module which we just import here, which would be a lot easier to - * read...except then where do we get the enums for the C code? Feh. - * - * OK, so maybe the plan is to define a new type, which has .name, - * .code, and .text attributes, all implemented via getsetters so - * they're read-only. Internal object would contain the enum, a const - * C char pointer for .name, and a PyObject for the .text attribute: - * we want to allocate the .text once, because some of those values - * come from OpenSSL; the others are trivial to convert to Python in - * the getter function. We allocate an object for each code, and - * stuff all of these into a sequence, which we don't need to expose, - * it's just there to protect these objects from garbage collection - * and to let us do a quick lookup from code -> object. Open question - * whether we want to allocate a separate module to hold these, - * probably not difficult. If we expose the sequence rather than - * hiding it, we can create the separate module in Python code from - * rpki/POW/__init__.py rather than having to do it in C. */ /* * There's some ugly C preprocessor junk here. Sorry, but it's the - * simplest way to keep all the definitions in a single place and expand - * them into all the forms we need in both C and Python. + * simplest way to keep all the definitions in a single place and + * expand them into all the forms we need in both C and Python. We + * undef the macros once we're done with them to localize the mess. */ /* @@ -1378,6 +1348,16 @@ status_code_object_str(status_code_object *self) return self->name; } +static PyObject * +status_code_object_repr(status_code_object *self) +{ + const char *text = PyString_AsString(self->text); + if (text == NULL) + return NULL; + return PyString_FromFormat("<%s object \"%s\" at %p>", self->ob_type->tp_name, text, self); +} + + static long status_code_object_hash(status_code_object *self) { @@ -1444,7 +1424,7 @@ static PyTypeObject POW_StatusCode_Type = { 0, /* tp_getattr */ 0, /* tp_setattr */ (cmpfunc) status_code_object_compare, /* tp_compare */ - 0, /* tp_repr */ + (reprfunc) status_code_object_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ @@ -1554,6 +1534,99 @@ record_validation_status(PyObject *status, const validation_status_t code) return result == 0; } +/* + * Validation callback code for use with x509_verify_cert(). + * + * In theory record_validation_status() can throw an exception, but + * there's no direct way to handle that here, so we're depending on + * x509_store_object_verify() calling PyErr_Occurred() to check (which + * it has to do anyway to handle exceptions generated by Python + * callback handlers. + */ +static int +validation_status_x509_verify_cert_cb(int ok, X509_STORE_CTX *ctx, PyObject *status) +{ + switch (ctx->error) { + case X509_V_OK: + return ok; + + case X509_V_ERR_SUBJECT_ISSUER_MISMATCH: + /* + * Informational events, not really errors. ctx->check_issued() + * is called in many places where failure to find an issuer is not + * a failure for the calling function. Just leave these alone. + */ + return ok; + + case X509_V_ERR_CRL_HAS_EXPIRED: + /* + * This isn't really an error, exactly. CRLs don't really + * "expire". What OpenSSL really means by this error is just + * "it's now later than the issuer said it intended to publish a + * new CRL". Whether we treat this as an error or not is + * configurable, see the allow_stale_crl parameter. + * + * Deciding whether to allow stale CRLs is check_crl_1()'s job, + * not ours. By the time this callback occurs, we've already + * accepted the CRL; this callback is just notifying us that the + * object being checked is tainted by a stale CRL. So we mark the + * object as tainted and carry on. + */ + record_validation_status(status, TAINTED_BY_STALE_CRL); + return 1; + + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + /* + * This is another error that's only an error in the strange world + * of OpenSSL, but a more serious one. By default, OpenSSL + * expects all trust anchors to be self-signed. This is not a + * PKIX requirement, it's just an OpenSSL thing, but one violates + * it at one's peril, because the only way to convince OpenSSL to + * allow a non-self-signed trust anchor is to intercept this + * "error" in the verify callback handler. + * + * So this program supports non-self-signed trust anchors, but be + * warned that enabling this feature may cause this program's + * output not to work with other OpenSSL-based applications. + */ +#if 0 + if (allow_non_self_signed_trust_anchor) + ok = 1; +#else +#warning Need some kind of global function to control configurable validation stuff like this. +#endif + record_validation_status(status, TRUST_ANCHOR_NOT_SELF_SIGNED); + return ok; + + /* + * Handle known OpenSSL verify errors except the ones we handle + * explicitly above. + */ +#define QV(x) \ + case x: \ + record_validation_status(status, STATUS_CODE_##x); \ + return ok; + VALIDATION_STATUS_CODES_FROM_OPENSSL; +#undef QV + + default: + /* + * If you see this, it means that OpenSSL returned a status code + * we don't know about, so it's time to check the list of known + * OpenSSL verify errors to find out what's missing. + */ + record_validation_status(status, UNKNOWN_OPENSSL_VERIFY_ERROR); + return ok; + } +} + +/* + * Done with the sick macros. + */ + +#undef VALIDATION_STATUS_CODES +#undef VALIDATION_STATUS_CODES_FROM_OPENSSL + /* @@ -4558,15 +4631,19 @@ x509_store_object_verify(x509_store_object *self, PyObject *args) Py_XINCREF(x509); Py_XINCREF(chain); + Py_XINCREF(status); X509_STORE_CTX_set_cert(ctx->ctx, x509->x509); X509_STORE_CTX_set_chain(ctx->ctx, stack); + ctx->status = status; X509_STORE_CTX_set_verify_cb(ctx->ctx, x509_store_ctx_object_verify_cb); ok = X509_verify_cert(ctx->ctx); + ctx->status = NULL; X509_STORE_CTX_set_chain(ctx->ctx, NULL); X509_STORE_CTX_set_cert(ctx->ctx, NULL); + Py_XDECREF(status); Py_XDECREF(chain); Py_XDECREF(x509); @@ -4654,8 +4731,17 @@ x509_store_ctx_object_verify_cb(int ok, X509_STORE_CTX *ctx) static char method_name[] = "verify_callback"; PyObject *self = X509_STORE_CTX_get_ex_data(ctx, x509_store_ctx_ex_data_idx); PyObject *result = NULL; + PyObject *status = NULL; + + if (self == NULL) + return ok; + + status = ((x509_store_ctx_object *) self)->status; + + if (status != NULL && status != Py_None) + ok = validation_status_x509_verify_cert_cb(ok, ctx, status); - if (self == NULL || !PyObject_HasAttrString(self, method_name)) + if (!PyObject_HasAttrString(self, method_name)) return ok; if ((result = PyObject_CallMethod(self, method_name, "i", ok)) == NULL) @@ -4678,6 +4764,7 @@ x509_store_ctx_object_new(PyTypeObject *type, GCC_UNUSED PyObject *args, GCC_UNU self->ctx = NULL; self->store = NULL; + self->status = NULL; return (PyObject *) self; error: |