/* * x509name.c * * Copyright (C) AB Strakt * Copyright (C) Jean-Paul Calderone * See LICENSE for details. * * X.509 Name handling, mostly thin wrapping. * See the file RATIONALE for a short explanation of why this module was written. * * Reviewed 2001-07-23 */ #include #define crypto_MODULE #include "crypto.h" static PyMethodDef crypto_X509Name_methods[4]; /* * Constructor for X509Name, never called by Python code directly * * Arguments: name - A "real" X509_NAME object * dealloc - Boolean value to specify whether the destructor should * free the "real" X509_NAME object * Returns: The newly created X509Name object */ crypto_X509NameObj * crypto_X509Name_New(X509_NAME *name, int dealloc) { crypto_X509NameObj *self; self = PyObject_GC_New(crypto_X509NameObj, &crypto_X509Name_Type); if (self == NULL) return NULL; self->x509_name = name; self->dealloc = dealloc; self->parent_cert = NULL; PyObject_GC_Track(self); return self; } static char crypto_X509Name_doc[] = "\n\ X509Name(name) -> New X509Name object\n\ \n\ Create a new X509Name, copying the given X509Name instance.\n\ \n\ :param name: An X509Name object to copy\n\ :return: The X509Name object\n\ "; static PyObject * crypto_X509Name_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) { crypto_X509NameObj *name; if (!PyArg_ParseTuple(args, "O!:X509Name", &crypto_X509Name_Type, &name)) { return NULL; } return (PyObject *)crypto_X509Name_New(X509_NAME_dup(name->x509_name), 1); } /* * Return a name string given a X509_NAME object and a name identifier. Used * by the getattr function. * * Arguments: name - The X509_NAME object * nid - The name identifier * Returns: The name as a Python string object */ static int get_name_by_nid(X509_NAME *name, int nid, char **utf8string) { int entry_idx; X509_NAME_ENTRY *entry; ASN1_STRING *data; int len; if ((entry_idx = X509_NAME_get_index_by_NID(name, nid, -1)) == -1) { return 0; } entry = X509_NAME_get_entry(name, entry_idx); data = X509_NAME_ENTRY_get_data(entry); if ((len = ASN1_STRING_to_UTF8((unsigned char **)utf8string, data)) < 0) { exception_from_error_queue(crypto_Error); return -1; } return len; } /* * Given a X509_NAME object and a name identifier, set the corresponding * attribute to the given string. Used by the setattr function. * * Arguments: name - The X509_NAME object * nid - The name identifier * value - The string to set * Returns: 0 for success, -1 on failure */ static int set_name_by_nid(X509_NAME *name, int nid, char *utf8string) { X509_NAME_ENTRY *ne; int i, entry_count, temp_nid; /* If there's an old entry for this NID, remove it */ entry_count = X509_NAME_entry_count(name); for (i = 0; i < entry_count; i++) { ne = X509_NAME_get_entry(name, i); temp_nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(ne)); if (temp_nid == nid) { ne = X509_NAME_delete_entry(name, i); X509_NAME_ENTRY_free(ne); break; } } /* Add the new entry */ if (!X509_NAME_add_entry_by_NID(name, nid, MBSTRING_UTF8, (unsigned char *)utf8string, -1, -1, 0)) { exception_from_error_queue(crypto_Error); return -1; } return 0; } /* * Find attribute. An X509Name object has the following attributes: * countryName (alias C), stateOrProvince (alias ST), locality (alias L), * organization (alias O), organizationalUnit (alias OU), commonName (alias * CN) and more... * * Arguments: self - The X509Name object * name - The attribute name * Returns: A Python object for the attribute, or NULL if something went * wrong */ static PyObject * crypto_X509Name_getattro(crypto_X509NameObj *self, PyObject *nameobj) { int nid, len; char *utf8string; char *name; #ifdef PY3 name = PyBytes_AsString(PyUnicode_AsASCIIString(nameobj)); #else name = PyBytes_AsString(nameobj); #endif if ((nid = OBJ_txt2nid(name)) == NID_undef) { /* * This is a bit weird. OBJ_txt2nid indicated failure, but it seems * a lower level function, a2d_ASN1_OBJECT, also feels the need to * push something onto the error queue. If we don't clean that up * now, someone else will bump into it later and be quite confused. * See lp#314814. */ flush_error_queue(); return PyObject_GenericGetAttr((PyObject*)self, nameobj); } len = get_name_by_nid(self->x509_name, nid, &utf8string); if (len < 0) return NULL; else if (len == 0) { Py_INCREF(Py_None); return Py_None; } else { PyObject* result = PyUnicode_Decode(utf8string, len, "utf-8", NULL); OPENSSL_free(utf8string); return result; } } /* * Set attribute * * Arguments: self - The X509Name object * name - The attribute name * value - The value to set */ static int crypto_X509Name_setattro(crypto_X509NameObj *self, PyObject *nameobj, PyObject *value) { int nid; int result; char *buffer; char *name; if (!PyBytes_CheckExact(nameobj) && !PyUnicode_CheckExact(nameobj)) { PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%.200s'", Py_TYPE(nameobj)->tp_name); return -1; } #ifdef PY3 name = PyBytes_AsString(PyUnicode_AsASCIIString(nameobj)); #else name = PyBytes_AsString(nameobj); #endif if ((nid = OBJ_txt2nid(name)) == NID_undef) { /* Just like the case in the getattr function */ flush_error_queue(); PyErr_SetString(PyExc_AttributeError, "No such attribute"); return -1; } /* Something of a hack to get nice unicode behaviour */ if (!PyArg_Parse(value, "es:setattr", "utf-8", &buffer)) return -1; result = set_name_by_nid(self->x509_name, nid, buffer); PyMem_Free(buffer); return result; } /* * Compare two X509Name structures. * * Arguments: n - The first X509Name * m - The second X509Name * Returns: <0 if n < m, 0 if n == m and >0 if n > m */ static PyObject * crypto_X509Name_richcompare(PyObject *n, PyObject *m, int op) { int result; if (!crypto_X509Name_Check(n) || !crypto_X509Name_Check(m)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } result = X509_NAME_cmp( ((crypto_X509NameObj*)n)->x509_name, ((crypto_X509NameObj*)m)->x509_name); switch (op) { case Py_EQ: result = (result == 0); break; case Py_NE: result = (result != 0); break; case Py_LT: result = (result < 0); break; case Py_LE: result = (result <= 0); break; case Py_GT: result = (result > 0); break; case Py_GE: result = (result >= 0); break; default: /* Should be impossible */ Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } if (result) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } /* * String representation of an X509Name * * Arguments: self - The X509Name object * Returns: A string representation of the object */ static PyObject * crypto_X509Name_repr(crypto_X509NameObj *self) { char tmpbuf[512] = ""; char realbuf[512+64]; if (X509_NAME_oneline(self->x509_name, tmpbuf, 512) == NULL) { exception_from_error_queue(crypto_Error); return NULL; } else { /* This is safe because tmpbuf is max 512 characters */ sprintf(realbuf, "", tmpbuf); return PyText_FromString(realbuf); } } static char crypto_X509Name_hash_doc[] = "\n\ Return the hash value of this name\n\ \n\ :return: None\n\ "; /* * First four bytes of the MD5 digest of the DER form of an X509Name. * * Arguments: self - The X509Name object * Returns: An integer giving the hash. */ static PyObject * crypto_X509Name_hash(crypto_X509NameObj *self, PyObject* args) { unsigned long hash; if (!PyArg_ParseTuple(args, ":hash")) { return NULL; } hash = X509_NAME_hash(self->x509_name); return PyLong_FromLong(hash); } static char crypto_X509Name_der_doc[] = "\n\ Return the DER encoding of this name\n\ \n\ :return: None\n\ "; /* * Arguments: self - The X509Name object * Returns: The DER form of an X509Name. */ static PyObject * crypto_X509Name_der(crypto_X509NameObj *self, PyObject *args) { if (!PyArg_ParseTuple(args, ":der")) { return NULL; } i2d_X509_NAME(self->x509_name, 0); return PyBytes_FromStringAndSize(self->x509_name->bytes->data, self->x509_name->bytes->length); } static char crypto_X509Name_get_components_doc[] = "\n\ Returns the split-up components of this name.\n\ \n\ :return: List of tuples (name, value).\n\ "; static PyObject * crypto_X509Name_get_components(crypto_X509NameObj *self, PyObject *args) { int n, i; X509_NAME *name = self->x509_name; PyObject *list; if (!PyArg_ParseTuple(args, ":get_components")) return NULL; n = X509_NAME_entry_count(name); list = PyList_New(n); for (i = 0; i < n; i++) { X509_NAME_ENTRY *ent; ASN1_OBJECT *fname; ASN1_STRING *fval; int nid; int l; unsigned char *str; PyObject *tuple; ent = X509_NAME_get_entry(name, i); fname = X509_NAME_ENTRY_get_object(ent); fval = X509_NAME_ENTRY_get_data(ent); l = ASN1_STRING_length(fval); str = ASN1_STRING_data(fval); nid = OBJ_obj2nid(fname); /* printf("fname is %s len=%d str=%s\n", OBJ_nid2sn(nid), l, str); */ tuple = PyTuple_New(2); PyTuple_SetItem(tuple, 0, PyBytes_FromString(OBJ_nid2sn(nid))); PyTuple_SetItem(tuple, 1, PyBytes_FromStringAndSize((char *)str, l)); PyList_SetItem(list, i, tuple); } return list; } /* * Call the visitproc on all contained objects. * * Arguments: self - The Connection object * visit - Function to call * arg - Extra argument to visit * Returns: 0 if all goes well, otherwise the return code from the first * call that gave non-zero result. */ static int crypto_X509Name_traverse(crypto_X509NameObj *self, visitproc visit, void *arg) { int ret = 0; if (ret == 0 && self->parent_cert != NULL) ret = visit(self->parent_cert, arg); return ret; } /* * Decref all contained objects and zero the pointers. * * Arguments: self - The Connection object * Returns: Always 0. */ static int crypto_X509Name_clear(crypto_X509NameObj *self) { Py_XDECREF(self->parent_cert); self->parent_cert = NULL; return 0; } /* * Deallocate the memory used by the X509Name object * * Arguments: self - The X509Name object * Returns: None */ static void crypto_X509Name_dealloc(crypto_X509NameObj *self) { PyObject_GC_UnTrack(self); /* Sometimes we don't have to dealloc this */ if (self->dealloc) X509_NAME_free(self->x509_name); crypto_X509Name_clear(self); PyObject_GC_Del(self); } /* * ADD_METHOD(name) expands to a correct PyMethodDef declaration * { 'name', (PyCFunction)crypto_X509_name, METH_VARARGS } * for convenience */ #define ADD_METHOD(name) \ { #name, (PyCFunction)crypto_X509Name_##name, METH_VARARGS, crypto_X509Name_##name##_doc } static PyMethodDef crypto_X509Name_methods[] = { ADD_METHOD(hash), ADD_METHOD(der), ADD_METHOD(get_components), { NULL, NULL } }; #undef ADD_METHOD PyTypeObject crypto_X509Name_Type = { PyOpenSSL_HEAD_INIT(&PyType_Type, 0) "X509Name", sizeof(crypto_X509NameObj), 0, (destructor)crypto_X509Name_dealloc, NULL, /* print */ NULL, /* getattr */ NULL, /* setattr */ NULL, /* reserved */ (reprfunc)crypto_X509Name_repr, NULL, /* as_number */ NULL, /* as_sequence */ NULL, /* as_mapping */ NULL, /* hash */ NULL, /* call */ NULL, /* str */ (getattrofunc)crypto_X509Name_getattro, /* getattro */ (setattrofunc)crypto_X509Name_setattro, /* setattro */ NULL, /* as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ crypto_X509Name_doc, /* tp_doc */ (traverseproc)crypto_X509Name_traverse, /* tp_traverse */ (inquiry)crypto_X509Name_clear, /* tp_clear */ crypto_X509Name_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ NULL, /* tp_iter */ NULL, /* tp_iternext */ crypto_X509Name_methods, /* tp_methods */ NULL, /* tp_members */ NULL, /* tp_getset */ NULL, /* tp_base */ NULL, /* tp_dict */ NULL, /* tp_descr_get */ NULL, /* tp_descr_set */ 0, /* tp_dictoffset */ NULL, /* tp_init */ NULL, /* tp_alloc */ crypto_X509Name_new, /* tp_new */ }; /* * Initialize the X509Name part of the crypto module * * Arguments: module - The crypto module * Returns: None */ int init_crypto_x509name(PyObject *module) { if (PyType_Ready(&crypto_X509Name_Type) < 0) { return 0; } /* PyModule_AddObject steals a reference. */ Py_INCREF((PyObject *)&crypto_X509Name_Type); if (PyModule_AddObject(module, "X509Name", (PyObject *)&crypto_X509Name_Type) != 0) { return 0; } /* PyModule_AddObject steals a reference. */ Py_INCREF((PyObject *)&crypto_X509Name_Type); if (PyModule_AddObject(module, "X509NameType", (PyObject *)&crypto_X509Name_Type) != 0) { return 0; } return 1; }