/* radare2 - LGPL - Copyright 2017 - wargio */ #include #include #include #include #include "r_x509_internal.h" bool r_x509_parse_validity (RX509Validity *validity, RASN1Object *object) { RASN1Object *o; if (!validity || !object || object->list.length != 2) { return false; } if (object->klass == CLASS_UNIVERSAL && object->tag == TAG_SEQUENCE && object->form == FORM_CONSTRUCTED) { o = object->list.objects[0]; if (o->klass == CLASS_UNIVERSAL && o->form == FORM_PRIMITIVE) { if (o->tag == TAG_UTCTIME) { validity->notBefore = r_asn1_stringify_utctime (o->sector, o->length); } else if (o->tag == TAG_GENERALIZEDTIME) { validity->notBefore = r_asn1_stringify_time (o->sector, o->length); } } o = object->list.objects[1]; if (o->klass == CLASS_UNIVERSAL && o->form == FORM_PRIMITIVE) { if (o->tag == TAG_UTCTIME) { validity->notAfter = r_asn1_stringify_utctime (o->sector, o->length); } else if (o->tag == TAG_GENERALIZEDTIME) { validity->notAfter = r_asn1_stringify_time (o->sector, o->length); } } } return true; } bool r_x509_parse_algorithmidentifier (RX509AlgorithmIdentifier *ai, RASN1Object * object) { if (!ai || !object || object->list.length < 1 || !object->list.objects) { return false; } if (object->list.objects[0] && object->list.objects[0]->klass == CLASS_UNIVERSAL && object->list.objects[0]->tag == TAG_OID) { ai->algorithm = r_asn1_stringify_oid (object->list.objects[0]->sector, object->list.objects[0]->length); } ai->parameters = NULL; // TODO //ai->parameters = asn1_stringify_sector (object->list.objects[1]); return true; } bool r_x509_parse_subjectpublickeyinfo (RX509SubjectPublicKeyInfo * spki, RASN1Object *object) { RASN1Object *o; if (!spki || !object || object->list.length != 2) { return false; } r_x509_parse_algorithmidentifier (&spki->algorithm, object->list.objects[0]); if (object->list.objects[1]) { o = object->list.objects[1]; R_PTR_MOVE (spki->subjectPublicKey, object->list.objects[1]); // spki->subjectPublicKey = object->list.objects[1]; // object->list.objects[1] = NULL; // if (o->length > 32) { // spki->subjectPublicKey = asn1_stringify_bytes (o->sector, o->length); // } else { // spki->subjectPublicKey = asn1_stringify_bits (o->sector, o->length); // } if (o->list.length == 1 && o->list.objects[0]->list.length == 2) { o = o->list.objects[0]; if (o->list.objects[0]) { R_PTR_MOVE (spki->subjectPublicKeyExponent, o->list.objects[0]); // o->list.objects[0] = NULL; // if (o->list.objects[0]->length > 32) { // spki->subjectPublicKeyExponent = asn1_stringify_bytes (o->list.objects[0]->sector, o->list.objects[0]->length); // } else { // spki->subjectPublicKeyExponent = asn1_stringify_integer (o->list.objects[0]->sector, o->list.objects[0]->length); // } } if (o->list.objects[1]) { R_PTR_MOVE (spki->subjectPublicKeyModule, o->list.objects[1]); // spki->subjectPublicKeyModule = o->list.objects[1]; // o->list.objects[1] = NULL; // spki->subjectPublicKeyModule = asn1_stringify_integer (o->list.objects[1]->sector, o->list.objects[1]->length); } } } return true; } bool r_x509_parse_name (RX509Name *name, RASN1Object * object) { ut32 i; if (!name || !object || !object->list.length) { return false; } if (object->klass == CLASS_UNIVERSAL && object->tag == TAG_SEQUENCE) { name->length = object->list.length; name->names = (RASN1String**) calloc (name->length, sizeof (RASN1String*)); if (!name->names) { name->length = 0; return false; } name->oids = (RASN1String**) calloc (name->length, sizeof (RASN1String*)); if (!name->oids) { name->length = 0; free (name->names); name->names = NULL; return false; } for (i = 0; i < object->list.length; ++i) { RASN1Object *o = object->list.objects[i]; if (o && o->klass == CLASS_UNIVERSAL && o->tag == TAG_SET && o->form == FORM_CONSTRUCTED && o->list.length == 1) { o = o->list.objects[0]; if (o && o->list.length > 1 && o->klass == CLASS_UNIVERSAL && o->tag == TAG_SEQUENCE) { if (o->list.objects[0]->klass == CLASS_UNIVERSAL && o->list.objects[0]->tag == TAG_OID) { name->oids[i] = r_asn1_stringify_oid (o->list.objects[0]->sector, o->list.objects[0]->length); } if (o->list.objects[1]->klass == CLASS_UNIVERSAL) { name->names[i] = r_asn1_stringify_string (o->list.objects[1]->sector, o->list.objects[1]->length); } } } } } return true; } bool r_x509_parse_extension (RX509Extension *ext, RASN1Object *object) { RASN1Object *o; if (!ext || !object || object->list.length < 2) { return false; } o = object->list.objects[0]; if (o && o->tag == TAG_OID) { ext->extnID = r_asn1_stringify_oid (o->sector, o->length); o = object->list.objects[1]; if (o->tag == TAG_BOOLEAN) { //This field is optional (so len must be 3) ext->critical = o->sector[0] != 0; o = object->list.objects[2]; } if (o->tag == TAG_OCTETSTRING) { ext->extnValue = o; if (o == object->list.objects[1]) { object->list.objects[1] = NULL; } else if (object->list.length > 2 && o == object->list.objects[2]) { object->list.objects[2] = NULL; } } } return true; } bool r_x509_parse_extensions (RX509Extensions *ext, RASN1Object * object) { ut32 i; if (!ext || !object || object->list.length != 1 || !object->list.objects[0]->length) { return false; } object = object->list.objects[0]; ext->extensions = (RX509Extension**) calloc (object->list.length, sizeof (RX509Extension*)); if (!ext->extensions) { return false; } ext->length = object->list.length; for (i = 0; i < object->list.length; ++i) { ext->extensions[i] = R_NEW0 (RX509Extension); if (!r_x509_parse_extension (ext->extensions[i], object->list.objects[i])) { r_x509_free_extension(ext->extensions[i]); ext->extensions[i] = NULL; } } return true; } bool r_x509_parse_tbscertificate (RX509TBSCertificate *tbsc, RASN1Object * object) { RASN1Object **elems; ut32 i; ut32 shift = 0; if (!tbsc || !object || object->list.length < 6) { return false; } elems = object->list.objects; //Following RFC if (elems[0]->list.length == 1 && elems[0]->klass == CLASS_CONTEXT && elems[0]->form == FORM_CONSTRUCTED && elems[0]->list.objects[0]->tag == TAG_INTEGER && elems[0]->list.objects[0]->length == 1) { //Integer inside a CLASS_CONTEXT tbsc->version = (ut32) elems[0]->list.objects[0]->sector[0]; shift = 1; } else { tbsc->version = 0; } if (shift < object->list.length && elems[shift]->klass == CLASS_UNIVERSAL && elems[shift]->tag == TAG_INTEGER) { tbsc->serialNumber = r_asn1_stringify_integer (elems[shift]->sector, elems[shift]->length); } r_x509_parse_algorithmidentifier (&tbsc->signature, elems[shift + 1]); r_x509_parse_name (&tbsc->issuer, elems[shift + 2]); r_x509_parse_validity (&tbsc->validity, elems[shift + 3]); r_x509_parse_name (&tbsc->subject, elems[shift + 4]); r_x509_parse_subjectpublickeyinfo (&tbsc->subjectPublicKeyInfo, elems[shift + 5]); if (tbsc->version > 0) { for (i = shift + 6; i < object->list.length; i++) { if (!elems[i] || elems[i]->klass != CLASS_CONTEXT) { continue; } if (elems[i]->tag == 1) { R_PTR_MOVE (tbsc->issuerUniqueID, object->list.objects[i]); // tbsc->issuerUniqueID = elems[i]; // elems[i] = NULL; } if (!elems[i]) { continue; } if (elems[i]->tag == 2) { R_PTR_MOVE (tbsc->subjectUniqueID, object->list.objects[i]); // tbsc->subjectUniqueID = elems[i]; // elems[i] = NULL; } if (!elems[i]) { continue; } if (tbsc->version == 2 && elems[i]->tag == 3 && elems[i]->form == FORM_CONSTRUCTED) { r_x509_parse_extensions (&tbsc->extensions, elems[i]); } } } return true; } RX509Certificate * r_x509_parse_certificate (RASN1Object *object) { RX509Certificate *certificate; RASN1Object *tmp; if (!object) { return NULL; } certificate = (RX509Certificate*) malloc (sizeof (RX509Certificate)); if (!certificate) { r_asn1_free_object (object); return NULL; } memset (certificate, 0, sizeof (RX509Certificate)); if (object->klass != CLASS_UNIVERSAL || object->form != FORM_CONSTRUCTED || object->list.length != 3) { // Malformed certificate // It needs to have tbsCertificate, algorithmIdentifier and a signature r_asn1_free_object (object); free (certificate); return NULL; } tmp = object->list.objects[2]; if (!tmp) { r_asn1_free_object (object); free (certificate); return NULL; } if (tmp->klass != CLASS_UNIVERSAL || tmp->form != FORM_PRIMITIVE || tmp->tag != TAG_BITSTRING) { r_asn1_free_object (object); free (certificate); return NULL; } R_PTR_MOVE (certificate->signature, object->list.objects[2]); // certificate->signature = object->list.objects[2]; // object->list.objects[2] = NULL; r_x509_parse_tbscertificate (&certificate->tbsCertificate, object->list.objects[0]); if (!r_x509_parse_algorithmidentifier (&certificate->algorithmIdentifier, object->list.objects[1])) { r_asn1_free_object (object); free (certificate); return NULL; } r_asn1_free_object (object); return certificate; } RX509Certificate * r_x509_parse_certificate2 (const ut8 *buffer, ut32 length) { RX509Certificate *certificate; RASN1Object *object; if (!buffer || !length) { return NULL; } object = r_asn1_create_object (buffer, length); certificate = r_x509_parse_certificate (object); //object freed by r_x509_parse_certificate return certificate; } RX509CRLEntry *r_x509_parse_crlentry (RASN1Object *object) { RX509CRLEntry *entry; if (!object || object->list.length != 2) { return NULL; } entry = (RX509CRLEntry *) malloc (sizeof (RX509CRLEntry)); if (!entry) { return NULL; } entry->userCertificate = object->list.objects[0]; object->list.objects[0] = NULL; entry->revocationDate = r_asn1_stringify_utctime (object->list.objects[1]->sector, object->list.objects[1]->length); return entry; } RX509CertificateRevocationList* r_x509_parse_crl (RASN1Object *object) { RX509CertificateRevocationList *crl; RASN1Object **elems; if (!object || object->list.length < 4) { return NULL; } crl = (RX509CertificateRevocationList *) malloc (sizeof (RX509CertificateRevocationList)); if (!crl) { return NULL; } memset (crl, 0, sizeof (RX509CertificateRevocationList)); elems = object->list.objects; r_x509_parse_algorithmidentifier (&crl->signature, elems[0]); r_x509_parse_name (&crl->issuer, elems[1]); crl->lastUpdate = r_asn1_stringify_utctime (elems[2]->sector, elems[2]->length); crl->nextUpdate = r_asn1_stringify_utctime (elems[3]->sector, elems[3]->length); if (object->list.length > 4) { ut32 i; crl->revokedCertificates = calloc (object->list.objects[4]->list.length, sizeof (RX509CRLEntry*)); if (!crl->revokedCertificates) { free (crl); return NULL; } crl->length = object->list.objects[4]->list.length; for (i = 0; i < object->list.objects[4]->list.length; ++i) { crl->revokedCertificates[i] = r_x509_parse_crlentry (object->list.objects[4]->list.objects[i]); } } return crl; } void r_x509_free_algorithmidentifier (RX509AlgorithmIdentifier * ai) { if (!ai) { return; } r_asn1_free_string (ai->algorithm); r_asn1_free_string (ai->parameters); //no need to free ai, since this functions is used internally } void r_x509_free_validity (RX509Validity * validity) { if (!validity) { return; } r_asn1_free_string (validity->notAfter); r_asn1_free_string (validity->notBefore); // not freeing validity since it's not allocated dinamically } void r_x509_free_name (RX509Name * name) { ut32 i; if (!name) { return; } if (name->names) { for (i = 0; i < name->length; ++i) { r_asn1_free_string (name->oids[i]); r_asn1_free_string (name->names[i]); } R_FREE (name->names); R_FREE (name->oids); } // not freeing name since it's not allocated dinamically } void r_x509_free_extension (RX509Extension * ex) { if (ex) { r_asn1_free_string (ex->extnID); r_asn1_free_object (ex->extnValue); //this is allocated dinamically so, i'll free free (ex); } } void r_x509_free_extensions (RX509Extensions * ex) { ut32 i; if (!ex) { return; } if (ex->extensions) { for (i = 0; i < ex->length; ++i) { r_x509_free_extension (ex->extensions[i]); } free (ex->extensions); } //no need to free ex, since this functions is used internally } void r_x509_free_subjectpublickeyinfo (RX509SubjectPublicKeyInfo * spki) { if (spki) { r_x509_free_algorithmidentifier (&spki->algorithm); r_asn1_free_object (spki->subjectPublicKey); r_asn1_free_object (spki->subjectPublicKeyExponent); r_asn1_free_object (spki->subjectPublicKeyModule); // No need to free spki, since it's a static variable. } } void r_x509_free_tbscertificate (RX509TBSCertificate * tbsc) { if (tbsc) { // version is ut32 r_asn1_free_string (tbsc->serialNumber); r_x509_free_algorithmidentifier (&tbsc->signature); r_x509_free_name (&tbsc->issuer); r_x509_free_validity (&tbsc->validity); r_x509_free_name (&tbsc->subject); r_x509_free_subjectpublickeyinfo (&tbsc->subjectPublicKeyInfo); r_asn1_free_object (tbsc->subjectUniqueID); r_asn1_free_object (tbsc->issuerUniqueID); r_x509_free_extensions (&tbsc->extensions); //no need to free tbsc, since this functions is used internally } } void r_x509_free_certificate (RX509Certificate * certificate) { if (certificate) { r_asn1_free_object (certificate->signature); r_x509_free_algorithmidentifier (&certificate->algorithmIdentifier); r_x509_free_tbscertificate (&certificate->tbsCertificate); free (certificate); } } void r_x509_free_crlentry (RX509CRLEntry *entry) { if (entry) { r_asn1_free_object (entry->userCertificate); r_asn1_free_string (entry->revocationDate); free (entry); } } void r_x509_free_crl (RX509CertificateRevocationList *crl) { ut32 i; if (crl) { r_x509_free_algorithmidentifier (&crl->signature); r_x509_free_name (&crl->issuer); r_asn1_free_string (crl->nextUpdate); r_asn1_free_string (crl->lastUpdate); if (crl->revokedCertificates) { for (i = 0; i < crl->length; ++i) { r_x509_free_crlentry (crl->revokedCertificates[i]); crl->revokedCertificates[i] = NULL; } free (crl->revokedCertificates); crl->revokedCertificates = NULL; } free (crl); } } char* r_x509_validity_dump (RX509Validity* validity, char* buffer, ut32 length, const char* pad) { int p; if (!validity || !buffer || !length) { return NULL; } if (!pad) pad = ""; const char* b = validity->notBefore ? validity->notBefore->string : "Missing"; const char* a = validity->notAfter ? validity->notAfter->string : "Missing"; p = snprintf (buffer, length, "%sNot Before: %s\n%sNot After: %s\n", pad, b, pad, a); return p < 0 ? NULL : buffer + (ut32) p; } char* r_x509_name_dump (RX509Name* name, char* buffer, ut32 length, const char* pad) { ut32 i, p, len; int r; char* c; if (!name || !buffer || !length) { return NULL; } if (!pad) { pad = ""; } len = length; c = buffer; if (!c) { return NULL; } for (i = 0, p = 0; i < name->length; ++i) { if (!name->oids[i] || !name->names[i]) { continue; } if (len <= p) { return NULL; } r = snprintf (c + p, len - p, "%s%s: %s\n", pad, name->oids[i]->string, name->names[i]->string); p += r; if (r < 0 || len < p) { return NULL; } } return c + p; } char* r_x509_subjectpublickeyinfo_dump (RX509SubjectPublicKeyInfo* spki, char* buffer, ut32 length, const char* pad) { int r; const char *a; if (!spki || !buffer || !length) { return NULL; } if (!pad) { pad = ""; } a = spki->algorithm.algorithm ? spki->algorithm.algorithm->string : "Missing"; RASN1String* m = NULL; if (spki->subjectPublicKeyModule) { m = r_asn1_stringify_integer (spki->subjectPublicKeyModule->sector, spki->subjectPublicKeyModule->length); } // RASN1String* e = r_asn1_stringify_bytes (spki->subjectPublicKeyExponent->sector, spki->subjectPublicKeyExponent->length); // r = snprintf (buffer, length, "%sAlgorithm: %s\n%sModule: %s\n%sExponent: %u bytes\n%s\n", pad, a, pad, m->string, // pad, spki->subjectPublicKeyExponent->length - 1, e->string); r = snprintf (buffer, length, "%sAlgorithm: %s\n%sModule: %s\n%sExponent: %u bytes\n", pad, a, pad, m ? m->string : "Missing", pad, spki->subjectPublicKeyExponent ? spki->subjectPublicKeyExponent->length - 1 : 0); r_asn1_free_string (m); // r_asn1_free_string (e); return r < 0 ? NULL : buffer + (ut32) r; } char* r_x509_extensions_dump (RX509Extensions* exts, char* buffer, ut32 length, const char* pad) { ut32 i, p, len; int r; char* c; if (!exts || !buffer || !length) { return NULL; } if (!pad) { pad = ""; } len = length; c = buffer; if (!c) { return NULL; } for (i = 0, p = 0, r = 0; i < exts->length; ++i) { //RASN1String *s; RX509Extension *e = exts->extensions[i]; if (!e) continue; //TODO handle extensions.. //s = r_asn1_stringify_bytes (e->extnValue->sector, e->extnValue->length); if (len < p) { return NULL; } r = snprintf (c + p, len - p, "%s%s: %s\n%s%u bytes\n", pad, e->extnID ? e->extnID->string : "Missing", e->critical ? "critical" : "", pad, e->extnValue ? e->extnValue->length : 0); p += r; //r_asn1_free_string (s); if (r < 0 || len <= p) { return NULL; } } return c + p; } char* r_x509_tbscertificate_dump (RX509TBSCertificate* tbsc, char* buffer, ut32 length, const char* pad) { RASN1String *sid = NULL, *iid = NULL; char *pad2, *tmp; ut32 p; int r; if (!tbsc || !buffer || !length) { return NULL; } if (!pad) { pad = ""; } pad2 = r_str_newf ("%s ", pad); if (!pad2) return NULL; r = snprintf (buffer, length, "%sVersion: v%u\n" "%sSerial Number:\n%s %s\n" "%sSignature Algorithm:\n%s %s\n" "%sIssuer:\n", pad, tbsc->version + 1, pad, pad, tbsc->serialNumber ? tbsc->serialNumber->string : "Missing", pad, pad, tbsc->signature.algorithm ? tbsc->signature.algorithm->string : "Missing", pad); p = (ut32) r; if (r < 0 || length <= p || !(tmp = r_x509_name_dump (&tbsc->issuer, buffer + p, length - p, pad2))) { free (pad2); return NULL; } p = tmp - buffer; if (length <= p) { free (pad2); return NULL; } r = snprintf (buffer + p, length - p, "%sValidity:\n", pad); p += r; if (r < 0 || length <= p || !(tmp = r_x509_validity_dump (&tbsc->validity, buffer + p, length - p, pad2))) { free (pad2); return NULL; } p = tmp - buffer; if (r < 0 || length <= p) { free (pad2); return NULL; } r = snprintf (buffer + p, length - p, "%sSubject:\n", pad); p += r; if (r < 0 || length <= p || !(tmp = r_x509_name_dump (&tbsc->subject, buffer + p, length - p, pad2))) { free (pad2); return NULL; } p = tmp - buffer; if (r < 0 || length <= p) { free (pad2); return NULL; } r = snprintf (buffer + p, length - p, "%sSubject Public Key Info:\n", pad); p += r; if (r < 0 || length <= p || !(tmp = r_x509_subjectpublickeyinfo_dump (&tbsc->subjectPublicKeyInfo, buffer + p, length - p, pad2))) { free (pad2); return NULL; } p = tmp - buffer; if (tbsc->issuerUniqueID) { iid = r_asn1_stringify_integer (tbsc->issuerUniqueID->sector, tbsc->issuerUniqueID->length); if (iid) { if (length <= p) { r_asn1_free_string (iid); free (pad2); return NULL; } r = snprintf (buffer + p, length - p, "%sIssuer Unique ID:\n%s %s", pad, pad, iid->string); p += r; } else { free (pad2); return NULL; } r_asn1_free_string (iid); } if (tbsc->subjectUniqueID) { sid = r_asn1_stringify_integer (tbsc->subjectUniqueID->sector, tbsc->subjectUniqueID->length); if (sid) { if (length <= p) { r_asn1_free_string (sid); free (pad2); return NULL; } r = snprintf (buffer + p, length - p, "%sSubject Unique ID:\n%s %s", pad, pad, sid->string); p += r; } else { free (pad2); return NULL; } r_asn1_free_string (sid); } if (r < 0 || length <= p) { free (pad2); return NULL; } r = snprintf (buffer + p, length - p, "%sExtensions:\n", pad); p += r; if (r < 0 || length <= p || !(tmp = r_x509_extensions_dump (&tbsc->extensions, buffer + p, length - p, pad2))) { free (pad2); return NULL; } free (pad2); return buffer + p; } char* r_x509_certificate_dump (RX509Certificate* certificate, char* buffer, ut32 length, const char* pad) { // RASN1String *signature, RASN1String *algo = NULL; ut32 p; int r; char *tbsc, *pad2; if (!certificate || !buffer || !length) { return NULL; } if (!pad) { pad = ""; } pad2 = r_str_newf ("%s ", pad); if (!pad2) { return NULL; } if ((r = snprintf (buffer, length, "%sTBSCertificate:\n", pad)) < 0) { return NULL; } p = (ut32) r; tbsc = r_x509_tbscertificate_dump (&certificate->tbsCertificate, buffer + p, length - p, pad2); p = tbsc - buffer; if (length <= p) { free (pad2); return NULL; } algo = certificate->algorithmIdentifier.algorithm; // signature = r_asn1_stringify_bytes (certificate->signature->sector, certificate->signature->length); // r = snprintf (buffer + p, length - p, "%sAlgorithm:\n%s%s\n%sSignature: %u bytes\n%s\n", // pad, pad2, algo ? algo->string : "", // pad, certificate->signature->length, signature ? signature->string : ""); r = snprintf (buffer + p, length - p, "%sAlgorithm:\n%s%s\n%sSignature: %u bytes\n", pad, pad2, algo ? algo->string : "", pad, certificate->signature->length); if (r < 0) { free (pad2); return NULL; } p += (ut32) r; free (pad2); // r_asn1_free_string (signature); return buffer + p; } char* r_x509_crlentry_dump (RX509CRLEntry *crle, char* buffer, ut32 length, const char* pad) { RASN1String *id = NULL, *utc = NULL; int r; if (!crle || !buffer || !length) { return NULL; } if (!pad) { pad = ""; } utc = crle->revocationDate; if (crle->userCertificate) { id = r_asn1_stringify_integer (crle->userCertificate->sector, crle->userCertificate->length); } r = snprintf (buffer, length, "%sUser Certificate:\n%s %s\n" "%sRevocation Date:\n%s %s\n", pad, pad, id ? id->string : "Missing", pad, pad, utc ? utc->string : "Missing"); return r < 0 ? NULL : buffer + (ut32) r; } char* r_x509_crl_dump (RX509CertificateRevocationList *crl, char* buffer, ut32 length, const char* pad) { RASN1String *algo = NULL, *last = NULL, *next = NULL; ut32 i, p; int r; char *tmp, *pad2, *pad3; if (!crl || !buffer || !length) { return NULL; } if (!pad) { pad = ""; } pad3 = r_str_newf ("%s ", pad); if (!pad3) return NULL; pad2 = pad3 + 2; algo = crl->signature.algorithm; last = crl->lastUpdate; next = crl->nextUpdate; r = snprintf (buffer, length, "%sCRL:\n%sSignature:\n%s%s\n%sIssuer\n", pad, pad2, pad3, algo ? algo->string : "", pad2); p = (ut32) r; if (r < 0 || !(tmp = r_x509_name_dump (&crl->issuer, buffer + p, length - p, pad3))) { free (pad3); return NULL; } p = tmp - buffer; if (length <= p) { free (pad3); return NULL; } r = snprintf (buffer + p, length - p, "%sLast Update: %s\n%sNext Update: %s\n%sRevoked Certificates:\n", pad2, last ? last->string : "Missing", pad2, next ? next->string : "Missing", pad2); p += (ut32) r; if (r < 0) { free (pad3); return NULL; } for (i = 0; i < crl->length; ++i) { if (length <= p || !(tmp = r_x509_crlentry_dump (crl->revokedCertificates[i], buffer + p, length - p, pad3))) { free (pad3); return NULL; } p = tmp - buffer; } free (pad3); return buffer + p; }