/** 
  @file  ssl_verify.c
  @author web, feh -- parts of code borrowed from Pavel Shramov; tx Peter Conrad
  @brief Compares 'hostname' against SubAltName DNS:hostname + DN: /CN=hostname
*/
#include "ucspissl.h"
#include "case.h"
#include "str.h"

int ssl_verify(SSL *ssl,const char *hostname,stralloc *dnsout)
{
  X509 *cert;
  STACK_OF(GENERAL_NAME) *extensions;
  const GENERAL_NAME *ext;
  char buf[SSL_NAME_LEN];
  char *dnsname = 0;
  int i;
  int num; 
  int len;
  int dname = 0;

#if (OPENSSL_VERSION_NUMBER > 0x30100000L)
  cert = SSL_get1_peer_certificate(ssl);
#else
  cert = SSL_get_peer_certificate(ssl);
#endif
  if (!cert) return -1;

  if (SSL_get_verify_result(ssl) != X509_V_OK) return -2;

  if (hostname) {
    if (!stralloc_copys(dnsout,"")) return 1;
    extensions = X509_get_ext_d2i(cert,NID_subject_alt_name,0,0);
    num = sk_GENERAL_NAME_num(extensions); 	/* num = 0, if no SAN extensions */

    for (i = 0; i < num; ++i) {
      ext = sk_GENERAL_NAME_value(extensions,i);
      if (ext->type == GEN_DNS) {
        if (ASN1_STRING_type(ext->d.dNSName) != V_ASN1_IA5STRING) continue;
#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) 
        dnsname = (char *)ASN1_STRING_data(ext->d.dNSName);
#else 
        dnsname = (char *)ASN1_STRING_get0_data(ext->d.dNSName);
#endif
        len = ASN1_STRING_length(ext->d.dNSName);
        if (len != str_len(dnsname)) continue;
        if (!stralloc_copyb(dnsout,dnsname,len)) return 1; 
        if (case_diffs((char *)hostname,dnsname) == 0) return 0;
        dname = 1;
      }
    }
    
    if (!dname) {
      X509_NAME_get_text_by_NID(X509_get_subject_name(cert),NID_commonName,buf,sizeof(buf));
      buf[SSL_NAME_LEN - 1] = 0;
      if (!stralloc_copyb(dnsout,buf,str_len(buf))) return 1; 
      if (case_diffs((char *)hostname,buf) == 0) return 0;
    }

    return -3;
  }
  return 0;
}
