Index: doc/tls.html
==================================================================
--- doc/tls.html
+++ doc/tls.html
@@ -254,13 +254,18 @@
SSL2, SSL3, TLS1, TLS1.1, TLS1.2, TLS1.3, or unknown.
sbits n
The number of bits used for the session key.
signatureHashAlgorithm algorithm
The signature hash algorithm.
- signature_type type
+ signatureType type
The signature type value.
- verification result
+ verifyDepth n
+ Maximum depth for the certificate chain verification.
+ Default is -1, to check all.
+ verifyMode list
+ List of certificate verification modes.
+ verifyResult result
Certificate verification result.
ca_names list
List of the Certificate Authorities used to create the certificate.
Index: generic/tls.c
==================================================================
--- generic/tls.c
+++ generic/tls.c
@@ -2100,14 +2100,42 @@
Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj("cipher", -1));
Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj(ciphers, -1));
}
/* Verify the X509 certificate presented by the peer */
- Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj("verification", -1));
+ Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj("verifyResult", -1));
Tcl_ListObjAppendElement(interp, objPtr,
Tcl_NewStringObj(X509_verify_cert_error_string(SSL_get_verify_result(statePtr->ssl)), -1));
+ /* Verify mode */
+ Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj("verifyMode", -1));
+ /* SSL_CTX_get_verify_mode(ctx) */
+ mode = SSL_get_verify_mode(statePtr->ssl);
+ if (mode && SSL_VERIFY_NONE) {
+ Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj("none", -1));
+ } else {
+ Tcl_Obj *listObjPtr = Tcl_NewListObj(0, NULL);
+ if (mode && SSL_VERIFY_PEER) {
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("peer", -1));
+ }
+ if (mode && SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("fail if no peer cert", -1));
+ }
+ if (mode && SSL_VERIFY_CLIENT_ONCE) {
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("client once", -1));
+ }
+ if (mode && SSL_VERIFY_POST_HANDSHAKE) {
+ Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("post handshake", -1));
+ }
+ Tcl_ListObjAppendElement(interp, objPtr, listObjPtr);
+ }
+
+ /* Verify mode depth */
+ Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj("verifyDepth", -1));
+ /* SSL_CTX_get_verify_depth(ctx) */
+ Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewIntObj(SSL_get_verify_depth(statePtr->ssl)));
+
/* Report the selected protocol as a result of the negotiation */
SSL_get0_alpn_selected(statePtr->ssl, &proto, &len);
Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj("alpn", -1));
Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj((char *)proto, (int) len));
Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj("protocol", -1));
@@ -2118,11 +2146,11 @@
if (objc == 2 ? SSL_get_peer_signature_nid(statePtr->ssl, &nid) : SSL_get_signature_nid(statePtr->ssl, &nid)) {
Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj(OBJ_nid2ln(nid), -1));
} else {
Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj("", -1));
}
- Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj("signature_type", -1));
+ Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj("signatureType", -1));
if (objc == 2 ? SSL_get_peer_signature_type_nid(statePtr->ssl, &nid) : SSL_get_signature_type_nid(statePtr->ssl, &nid)) {
Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj(OBJ_nid2ln(nid), -1));
} else {
Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj("", -1));
}
Index: generic/tlsX509.c
==================================================================
--- generic/tlsX509.c
+++ generic/tlsX509.c
@@ -31,47 +31,10 @@
static int max(int a, int b)
{
return (a > b) ? a : b;
}
-/*
- * ASN1_UTCTIME_tostr --
- */
-static char *
-ASN1_UTCTIME_tostr(ASN1_UTCTIME *tm)
-{
- static char bp[128];
- char *v;
- int gmt=0;
- static char *mon[12]={
- "Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec"};
- int i;
- int y=0,M=0,d=0,h=0,m=0,s=0;
-
- i=tm->length;
- v=(char *)tm->data;
-
- if (i < 10) goto err;
- if (v[i-1] == 'Z') gmt=1;
- for (i=0; i<10; i++)
- if ((v[i] > '9') || (v[i] < '0')) goto err;
- y= (v[0]-'0')*10+(v[1]-'0');
- if (y < 70) y+=100;
- M= (v[2]-'0')*10+(v[3]-'0');
- if ((M > 12) || (M < 1)) goto err;
- d= (v[4]-'0')*10+(v[5]-'0');
- h= (v[6]-'0')*10+(v[7]-'0');
- m= (v[8]-'0')*10+(v[9]-'0');
- if ((v[10] >= '0') && (v[10] <= '9') && (v[11] >= '0') && (v[11] <= '9'))
- s= (v[10]-'0')*10+(v[11]-'0');
-
- sprintf(bp,"%s %2d %02d:%02d:%02d %d%s", mon[M-1],d,h,m,s,y+1900,(gmt)?" GMT":"");
- return bp;
- err:
- return "Bad time value";
-}
-
/*
* Binary string to hex string
*/
int String_to_Hex(char* input, int len, char *output, int max) {
int count = 0;
@@ -90,11 +53,11 @@
*
* ------------------------------------------------*
* Converts a X509 certificate into a Tcl_Obj
* ------------------------------------------------*
*
- * Sideeffects:
+ * Side effects:
* None
*
* Result:
* A Tcl List Object representing the provided
* X509 certificate.
@@ -106,59 +69,60 @@
Tcl_Obj*
Tls_NewX509Obj(Tcl_Interp *interp, X509 *cert) {
Tcl_Obj *certPtr = Tcl_NewListObj(0, NULL);
BIO *bio;
- int n;
- unsigned long flags;
+ int nid, pknid, bits, num_of_exts, len;
+ uint32_t xflags;
char subject[BUFSIZ];
char issuer[BUFSIZ];
char serial[BUFSIZ];
char notBefore[BUFSIZ];
char notAfter[BUFSIZ];
char buffer[BUFSIZ];
- char certStr[CERT_STR_SIZE], *certStr_p;
- int certStr_len, toRead;
- unsigned char sha1_hash_binary[SHA_DIGEST_LENGTH];
- unsigned char sha256_hash_binary[SHA256_DIGEST_LENGTH];
- int nid, pknid, bits, num_of_exts, len;
- uint32_t xflags;
+ char certStr[CERT_STR_SIZE];
+ unsigned char md[EVP_MAX_MD_SIZE];
STACK_OF(GENERAL_NAME) *san;
- certStr[0] = 0;
- if ((bio = BIO_new(BIO_s_mem())) == NULL) {
- subject[0] = 0;
- issuer[0] = 0;
- serial[0] = 0;
- } else {
- flags = XN_FLAG_RFC2253 | ASN1_STRFLGS_UTF8_CONVERT;
+ certStr[0] = 0;
+ subject[0] = 0;
+ issuer[0] = 0;
+ serial[0] = 0;
+ notBefore[0] = 0;
+ notAfter[0] = 0;
+ if ((bio = BIO_new(BIO_s_mem())) != NULL) {
+ int n;
+ unsigned long flags = XN_FLAG_RFC2253 | ASN1_STRFLGS_UTF8_CONVERT;
flags &= ~ASN1_STRFLGS_ESC_MSB;
- X509_NAME_print_ex(bio, X509_get_subject_name(cert), 0, flags);
- n = BIO_read(bio, subject, min(BIO_pending(bio), BUFSIZ - 1));
- n = max(n, 0);
- subject[n] = 0;
- (void)BIO_flush(bio);
-
- X509_NAME_print_ex(bio, X509_get_issuer_name(cert), 0, flags);
- n = BIO_read(bio, issuer, min(BIO_pending(bio), BUFSIZ - 1));
- n = max(n, 0);
- issuer[n] = 0;
- (void)BIO_flush(bio);
-
- i2a_ASN1_INTEGER(bio, X509_get0_serialNumber(cert));
- n = BIO_read(bio, serial, min(BIO_pending(bio), BUFSIZ - 1));
- n = max(n, 0);
- serial[n] = 0;
- (void)BIO_flush(bio);
+ /* Get subject name */
+ if (X509_NAME_print_ex(bio, X509_get_subject_name(cert), 0, flags) > 0) {
+ n = BIO_read(bio, subject, min(BIO_pending(bio), BUFSIZ - 1));
+ subject[max(n, 0)] = 0;
+ (void)BIO_flush(bio);
+ }
+
+ /* Get issuer name */
+ if (X509_NAME_print_ex(bio, X509_get_issuer_name(cert), 0, flags) > 0) {
+ n = BIO_read(bio, issuer, min(BIO_pending(bio), BUFSIZ - 1));
+ issuer[max(n, 0)] = 0;
+ (void)BIO_flush(bio);
+ }
+
+ /* Get serial number */
+ if (i2a_ASN1_INTEGER(bio, X509_get0_serialNumber(cert)) > 0) {
+ n = BIO_read(bio, serial, min(BIO_pending(bio), BUFSIZ - 1));
+ serial[max(n, 0)] = 0;
+ (void)BIO_flush(bio);
+ }
/* Get certificate */
if (PEM_write_bio_X509(bio, cert)) {
- certStr_p = certStr;
- certStr_len = 0;
+ char *certStr_p = certStr;
+ int certStr_len = 0;
while (1) {
- toRead = min(BIO_pending(bio), CERT_STR_SIZE - certStr_len - 1);
+ int toRead = min(BIO_pending(bio), CERT_STR_SIZE - certStr_len - 1);
toRead = min(toRead, BUFSIZ);
if (toRead == 0) {
break;
}
dprintf("Reading %i bytes from the certificate...", toRead);
@@ -171,44 +135,54 @@
}
*certStr_p = '\0';
(void)BIO_flush(bio);
}
- /* All */
+ /* Get all cert info */
if (X509_print_ex(bio, cert, flags, 0)) {
char all[65536];
n = BIO_read(bio, all, min(BIO_pending(bio), 65535));
- n = max(n, 0);
- all[n] = 0;
+ all[max(n, 0)] = 0;
(void)BIO_flush(bio);
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("all", -1));
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj(all, n));
}
+
+ /* Get Validity - Not Before */
+ if (ASN1_TIME_print(bio, X509_get0_notBefore(cert))) {
+ n = BIO_read(bio, notBefore, min(BIO_pending(bio), BUFSIZ - 1));
+ notBefore[max(n, 0)] = 0;
+ (void)BIO_flush(bio);
+ }
+
+ /* Get Validity - Not After */
+ if (ASN1_TIME_print(bio, X509_get0_notAfter(cert))) {
+ n = BIO_read(bio, notAfter, min(BIO_pending(bio), BUFSIZ - 1));
+ notAfter[max(n, 0)] = 0;
+ (void)BIO_flush(bio);
+ }
BIO_free(bio);
}
- strcpy(notBefore, ASN1_UTCTIME_tostr(X509_get0_notBefore(cert)));
- strcpy(notAfter, ASN1_UTCTIME_tostr(X509_get0_notAfter(cert)));
-
/* Version */
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("version", -1));
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewLongObj(X509_get_version(cert)+1));
/* Signature algorithm */
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("signature", -1));
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj(OBJ_nid2ln(X509_get_signature_nid(cert)),-1));
-
+
/* SHA1 Fingerprint of cert - DER representation */
- X509_digest(cert, EVP_sha1(), sha1_hash_binary, &len);
- len = String_to_Hex(sha1_hash_binary, len, buffer, BUFSIZ);
+ X509_digest(cert, EVP_sha1(), md, &len);
+ len = String_to_Hex(md, len, buffer, BUFSIZ);
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("sha1_hash", -1));
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj(buffer, len));
/* SHA256 Fingerprint of cert - DER representation */
- X509_digest(cert, EVP_sha256(), sha256_hash_binary, &len);
- len = String_to_Hex(sha256_hash_binary, len, buffer, BUFSIZ);
+ X509_digest(cert, EVP_sha256(), md, &len);
+ len = String_to_Hex(md, len, buffer, BUFSIZ);
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("sha256_hash", -1));
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj(buffer, len));
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("subject", -1));
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj(subject, -1));
@@ -242,17 +216,17 @@
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj(OBJ_nid2ln(pknid),-1));
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("bits", -1));
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewIntObj(bits));
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("extension_flags", -1));
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewIntObj(xflags));
-
+
/* Public key - X509_get0_pubkey */
key = X509_get0_pubkey_bitstr(cert);
len = String_to_Hex(key->data, key->length, buffer, BUFSIZ);
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("publicKey", -1));
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj(buffer, len));
-
+
/* Check if cert was issued by CA cert issuer or self signed */
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("self_signed", -1));
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewBooleanObj(X509_check_issued(cert, cert) == X509_V_OK));
}
@@ -315,18 +289,17 @@
sk_GENERAL_NAME_pop_free(san, GENERAL_NAME_free);
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("subjectAltName", -1));
Tcl_ListObjAppendElement(interp, certPtr, namesPtr);
}
- /* Certificate Alias */
+ /* Certificate Alias as UTF-8 string */
{
unsigned char *bstring;
len = 0;
bstring = X509_alias_get0(cert, &len);
- len = String_to_Hex(bstring, len, buffer, BUFSIZ);
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("alias", -1));
- Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj(buffer, len));
+ Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj((char *)bstring, len));
}
/* Get Subject Key id, Authority Key id */
{
ASN1_OCTET_STRING *astring;
@@ -333,20 +306,20 @@
/* X509_keyid_get0 */
astring = X509_get0_subject_key_id(cert);
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("subjectKeyIdentifier", -1));
if (astring != NULL) {
len = String_to_Hex((char *)ASN1_STRING_get0_data(astring), ASN1_STRING_length(astring), buffer, BUFSIZ);
- Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewByteArrayObj(buffer, len));
+ Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj(buffer, len));
} else {
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("", -1));
}
astring = X509_get0_authority_key_id(cert);
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("authorityKeyIdentifier", -1));
if (astring != NULL) {
len = String_to_Hex((char *)ASN1_STRING_get0_data(astring), ASN1_STRING_length(astring), buffer, BUFSIZ);
- Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewByteArrayObj(buffer, len));
+ Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj(buffer, len));
} else {
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("", -1));
}
/* const GENERAL_NAMES *X509_get0_authority_issuer(cert);
@@ -387,8 +360,27 @@
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj(buffer, len));
} else {
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("", -1));
}
}
+
+ /* Certificate purposes */
+ {
+ Tcl_Obj *purpPtr = Tcl_NewListObj(0, NULL);
+ for (int j = 0; j < X509_PURPOSE_get_count(); j++) {
+ X509_PURPOSE *ptmp = X509_PURPOSE_get0(j);
+ Tcl_Obj *tmpPtr = Tcl_NewListObj(0, NULL);
+
+ for (int i = 0; i < 2; i++) {
+ int idret = X509_check_purpose(cert, X509_PURPOSE_get_id(ptmp), i);
+ Tcl_ListObjAppendElement(interp, tmpPtr, Tcl_NewStringObj(i ? "CA" : "nonCA", -1));
+ Tcl_ListObjAppendElement(interp, tmpPtr, Tcl_NewStringObj(idret ? "Yes" : "No", -1));
+ }
+ Tcl_ListObjAppendElement(interp, purpPtr, Tcl_NewStringObj(X509_PURPOSE_get0_name(ptmp), -1));
+ Tcl_ListObjAppendElement(interp, purpPtr, tmpPtr);
+ }
+ Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("certificatePurpose", -1));
+ Tcl_ListObjAppendElement(interp, certPtr, purpPtr);
+ }
return certPtr;
}