Logo Search packages:      
Sourcecode: jabberd14 version File versions  Download package

static int mio_tls_check_x509 ( mio  m,
char const *  id_on_xmppAddr,
const std::string &  log_id 
) [static]

verify an X.509 certificate chain

Parameters:
m the mio structure the certificate is verified for
id_on_xmppAddr the xmpp address the peer claims to be, and that should be checked
log_id which ID to use for logging if we have to log a message
Returns:
0 if the verification failed, 1 if it succeeded

Definition at line 1581 of file mio_tls.cc.

References j_strcmp(), j_strncmp(), log_notice(), log_warn(), mio_tls_asn1_tree, mio_tls_cert_match(), pool_free(), and mio_st::ssl.

Referenced by mio_ssl_verify().

                                                                                          {
    int ret = 0;
    int verification_result = 1;
    const gnutls_datum_t *cert_list = NULL;
    unsigned int cert_list_size = 0;

    /* get the certificates */
    cert_list = gnutls_certificate_get_peers(static_cast<gnutls_session_t>(m->ssl), &cert_list_size);
    if (cert_list == NULL || cert_list_size <= 0) {
      log_notice(log_id.c_str(), "Problem verifying certificate: No certificate was found!");
      return 0;
    }

    log_debug2(ZONE, LOGT_AUTH, "We have to verify %i certificates for %s", cert_list_size, id_on_xmppAddr);

    /* iterate on the certificates */
    for (int crt_index = 0; crt_index < cert_list_size; crt_index++) {
      gnutls_x509_crt_t cert = NULL;
      std::string cert_subject;

      /* initialize X.509 certificate structure */
      ret = gnutls_x509_crt_init(&cert);
      if (ret < 0) {
          log_warn(log_id.c_str(), "Problem initializing the certificate var. Therefore I cannot verify the certificate.");
          return 0;
      }

      /* XXX begin debugging only
       *
      std::ostringstream tmpfilename;
      tmpfilename << "/tmp/";
      if (id_on_xmppAddr != NULL) {
          tmpfilename << id_on_xmppAddr;
      }
      tmpfilename << "_" << crt_index << ".der";

      std::ofstream tmpfile(tmpfilename.str().c_str());

      for (int c=0; c<cert_list[crt_index].size; c++) {
          tmpfile.put(cert_list[crt_index].data[c]);  // write is not working because of libpth's definitions
      }

      tmpfile.close();
       *
       * XXX end debugging only */

      /* get this certificate */
      ret = gnutls_x509_crt_import(cert, &cert_list[crt_index], GNUTLS_X509_FMT_DER);
      if (ret < 0) {
          log_warn(log_id.c_str(), "Error in loading certificate %i: %s", crt_index, gnutls_strerror(ret));
          verification_result = 0;
          gnutls_x509_crt_deinit(cert);
          cert = NULL;
          break;
      }

      /* get the DN of the certificate */
      char name[1024];
      size_t name_len = sizeof(name);
      ret = gnutls_x509_crt_get_dn(cert, name, &name_len);
      if (ret < 0) {
          log_warn(log_id.c_str(), "Error accessing DN of certificate %i: %s", crt_index, gnutls_strerror(ret));
      } else {
          cert_subject = name;
      }
      log_debug2(ZONE, LOGT_AUTH, "verifying certificate: %s", cert_subject.c_str());

      /* for the first certificate we have to check the subjectAltNames */
      if (crt_index == 0 && id_on_xmppAddr != NULL) {
          int ext_count = 0;
          int found_matching_subjectAltName = 0;
          int found_any_subjectAltName = 0;
          int may_match_dNSName = 1;

          log_debug2(ZONE, LOGT_AUTH, "verifying first certificate in chain ...");

          /* only pure domains may be matched against dNSName */
          if (id_on_xmppAddr == NULL || strchr(id_on_xmppAddr, '@') != NULL || strchr(id_on_xmppAddr, '/') != NULL) {
            may_match_dNSName = 0;
          }

          /* verify id-on-xmppAddr and dNSName */
          do {
            unsigned char subjectAltName[2048];
            size_t subjectAltName_size = sizeof(subjectAltName);
            unsigned int is_critical = 0;

            ret = gnutls_x509_crt_get_extension_by_oid(cert, "2.5.29.17", ext_count, subjectAltName, &subjectAltName_size, &is_critical);
            if (ret < 0) {
                if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
                  log_debug2(ZONE, LOGT_AUTH, "no more subjectAltName extensions (%i)", ext_count);
                } else {
                  log_warn(log_id.c_str(), "error requesting %i-th subjectAltName: %s (%s)", ext_count, gnutls_strerror(ret), cert_subject.c_str());
                }
            } else {
                ASN1_TYPE subjectAltName_element = ASN1_TYPE_EMPTY;
                int cnt = 0;

                log_debug2(ZONE, LOGT_AUTH, "got a%s subjectAltName extension", is_critical ? " critical" : "");

                /* we got a subjectAltName */
                found_any_subjectAltName = 1;

                /* init subjectAltName_element */
                ret = asn1_create_element(mio_tls_asn1_tree, "PKIX1.SubjectAltName", &subjectAltName_element);
                if (ret != ASN1_SUCCESS) {
                  log_warn(log_id.c_str(), "error creating asn1 element for PKIX1.SubjectAltName: %s (%s)", libtasn1_strerror(ret), cert_subject.c_str());
                  break;
                }

                /* decode the extension */
                ret = asn1_der_decoding(&subjectAltName_element, subjectAltName, subjectAltName_size, NULL);
                if (ret != ASN1_SUCCESS) {
                  log_warn(log_id.c_str(), "error DER decoding subjectAltName extension: %s (%s)", libtasn1_strerror(ret), cert_subject.c_str());
                  asn1_delete_structure(&subjectAltName_element);
                  break;
                }

                /* subjectAltName is a sequence we have to iterate ... */
                for (cnt = 1; cnt < 1024 && !found_matching_subjectAltName; cnt++) {
                  char cnt_string[6];
                  char address_type[32];
                  int address_type_len = sizeof(address_type);

                  snprintf(cnt_string, sizeof(cnt_string), "?%i", cnt);

                  log_debug2(ZONE, LOGT_AUTH, "accessing subjectAltName element %s", cnt_string);

                  ret = asn1_read_value(subjectAltName_element, cnt_string, address_type, &address_type_len);
                  if (ret == ASN1_ELEMENT_NOT_FOUND) {
                      log_debug2(ZONE, LOGT_AUTH, "no more values in subjectAltName (%s)", cnt_string);
                      break;
                  }
                  if (ret != ASN1_SUCCESS) {
                      log_notice(log_id.c_str(), "error accessing type for %s in subjectAltName: %s (%s)", cnt_string, libtasn1_strerror(ret), cert_subject.c_str());
                      break;
                  }

                  log_debug2(ZONE, LOGT_AUTH, "... it is of type %s", address_type);

                  /* is it a dNSName? */
                  if (j_strncmp(address_type, "dNSName", 8) == 0) {
                      if (!may_match_dNSName) {
                        log_debug2(ZONE, LOGT_AUTH, "not checking dNSName, as we are not searching for a domain");
                      } else {
                        char access_string[14];
                        char dNSName[2048];
                        int dNSName_len = sizeof(dNSName);
                        pool compare_pool = NULL;

                        snprintf(access_string, sizeof(access_string), "%s.dNSName", cnt_string);

                        ret = asn1_read_value(subjectAltName_element, access_string, dNSName, &dNSName_len);
                        if (ret != ASN1_SUCCESS) {
                            log_notice(log_id.c_str(), "error accessing %s in subjectAltName: %s (%s)", access_string, libtasn1_strerror(ret), cert_subject.c_str());
                            break;
                        }

                        if (dNSName_len >= sizeof(dNSName)) {
                            log_notice(log_id.c_str(), "got a dNSName which is longer then %i B. Skipping ... (%s)", sizeof(dNSName), cert_subject.c_str());
                            break;
                        }

                        /* zero terminating the dNSName */
                        dNSName[dNSName_len] = 0;
                        log_debug2(ZONE, LOGT_AUTH, "found dNSName: %s", dNSName);

                        /* get a memory pool for doing comparisons */
                        compare_pool = pool_new();

                        /* compare the dNSName */
                        if (mio_tls_cert_match(compare_pool, dNSName, id_on_xmppAddr) == 0) {
                            found_matching_subjectAltName = 1;
                            log_debug2(ZONE, LOGT_AUTH, "match on dNSName: %s", dNSName);
                        }

                        /* free memory pool */
                        pool_free(compare_pool);
                        compare_pool = NULL;
                      }
                  } else if (j_strncmp(address_type, "otherName", 10) == 0) {
                      char access_string_type[24];
                      char access_string_value[22];
                      char otherNameType[1024];
                      int otherNameType_len = sizeof(otherNameType);
                      unsigned char otherNameValue[1024];
                      int otherNameValue_len = sizeof(otherNameValue);

                      snprintf(access_string_type, sizeof(access_string_type), "%s.otherName.type-id", cnt_string);
                      snprintf(access_string_value, sizeof(access_string_value), "%s.otherName.value", cnt_string);

                      /* get the OID of the otherName */
                      ret = asn1_read_value(subjectAltName_element, access_string_type, otherNameType, &otherNameType_len);
                      if (ret != ASN1_SUCCESS) {
                        log_notice(log_id.c_str(), "error accessing type information %s in subjectAltName: %s (%s)", access_string_type, libtasn1_strerror(ret), cert_subject.c_str());
                        break;
                      }

                      /* is it an id-on-xmppAddr */
                      if (j_strncmp(otherNameType, "1.3.6.1.5.5.7.8.5", 18) != 0) {
                        log_notice(log_id.c_str(), "ignoring unknown otherName in subjectAltName (%s)", cert_subject.c_str());
                        break;
                      }

                      /* get the value of the otherName */
                      ret = asn1_read_value(subjectAltName_element, access_string_value, otherNameValue, &otherNameValue_len);
                      if (ret != ASN1_SUCCESS) {
                        log_notice(log_id.c_str(), "error accessing value of othername %s in subjectAltName: %s (%s)", access_string_value, libtasn1_strerror(ret), cert_subject.c_str());
                        break;
                      }

                      /* okay we now have an UTF8String ... get the content */
                      {
                        ASN1_TYPE directoryString_element = ASN1_TYPE_EMPTY;
                        char thisIdOnXMPPaddr[3072];
                        int thisIdOnXMPPaddr_len = sizeof(thisIdOnXMPPaddr);
                        pool jid_pool = NULL;
                        jid cert_jid = NULL;

                        ret = asn1_create_element(mio_tls_asn1_tree, "PKIX1.DirectoryString", &directoryString_element);
                        if (ret != ASN1_SUCCESS) {
                            log_notice(log_id.c_str(), "error creating DirectoryString element: %s (%s)", libtasn1_strerror(ret), cert_subject.c_str());
                            asn1_delete_structure(&directoryString_element);
                            break;
                        }

                        ret = asn1_der_decoding(&directoryString_element, otherNameValue, otherNameValue_len, NULL);
                        if (ret != ASN1_SUCCESS) {
                            log_notice(log_id.c_str(), "error decoding DirectoryString: %s (%s)", libtasn1_strerror(ret), cert_subject.c_str());
                            asn1_delete_structure(&directoryString_element);
                            break;
                        }

                        ret = asn1_read_value(directoryString_element, "utf8String", thisIdOnXMPPaddr, &thisIdOnXMPPaddr_len);
                        if (ret != ASN1_SUCCESS) {
                            log_notice(log_id.c_str(), "error accessing utf8String of DirectoryString: %s (%s)", libtasn1_strerror(ret), cert_subject.c_str());
                            asn1_delete_structure(&directoryString_element);
                            break;
                        }

                        if (thisIdOnXMPPaddr_len >= sizeof(thisIdOnXMPPaddr)) {
                            log_notice(log_id.c_str(), "id-on-xmppAddr is %i B long ... ignoring (%s)", thisIdOnXMPPaddr_len, cert_subject.c_str());
                            asn1_delete_structure(&directoryString_element);
                            break;
                        }

                        /* zero-terminate the string */
                        thisIdOnXMPPaddr[thisIdOnXMPPaddr_len] = 0;

                        /* nameprep the domain */
                        jid_pool = pool_new();
                        cert_jid = jid_new(jid_pool, thisIdOnXMPPaddr);

                        if (cert_jid == NULL || cert_jid->server == NULL) {
                            cert_jid = NULL;
                            pool_free(jid_pool);
                            jid_pool = NULL;

                            log_notice(log_id.c_str(), "invalid id-on-xmppAddr: %s ... skipping this one (%s)", thisIdOnXMPPaddr, cert_subject.c_str());
                            break;
                        }

                        log_debug2(ZONE, LOGT_AUTH, "found id-on-xmppAddr: %s", jid_full(cert_jid));

                        /* compare */
                        if (j_strcmp(id_on_xmppAddr, jid_full(cert_jid)) == 0) {
                            found_matching_subjectAltName = 1;
                            log_debug2(ZONE, LOGT_AUTH, "match on id-on-xmppAddr: %s", thisIdOnXMPPaddr);
                        }

                        /* free memory needed for nameprepping */
                        cert_jid = NULL;
                        pool_free(jid_pool);
                        jid_pool = NULL;

                        /* free memory needed to DER decode utf8String */
                        asn1_delete_structure(&directoryString_element);
                      }

                  } else {
                      log_notice(log_id.c_str(), "ignoring %s in subjectAltName (%s)", address_type, cert_subject.c_str());
                  }
                }

                asn1_delete_structure(&subjectAltName_element);
            }

            ext_count++;
          } while (ret >= 0 && !found_matching_subjectAltName);

          if (found_any_subjectAltName) {
            if (!found_matching_subjectAltName) {
                log_notice(log_id.c_str(), "Found subjectAltName, but non matched (%s)", cert_subject.c_str());
                verification_result = 0;
                gnutls_x509_crt_deinit(cert);
                cert = NULL;
                break;
            }
          } else {
            /* verify subject */
            if (!gnutls_x509_crt_check_hostname(cert, id_on_xmppAddr)) {
                log_notice(log_id.c_str(), "Certificate subject does not match. (%s)", cert_subject.c_str());
                verification_result = 0;
                gnutls_x509_crt_deinit(cert);
                cert = NULL;
                break;
            }
          }
      }

      /* check expiration */
      if (gnutls_x509_crt_get_expiration_time(cert) < time(NULL)) {
          log_notice(log_id.c_str(), "Certificate %i has expired (%s)", crt_index, cert_subject.c_str());
          verification_result = 0;
          gnutls_x509_crt_deinit(cert);
          cert = NULL;
          break;
      }
      if (gnutls_x509_crt_get_activation_time(cert) > time(NULL)) {
          log_notice(log_id.c_str(), "Certificate %i not yet active (%s)", crt_index, cert_subject.c_str());
          verification_result = 0;
          gnutls_x509_crt_deinit(cert);
          cert = NULL;
          break;
      }
      gnutls_x509_crt_deinit(cert);
    }
    
    return verification_result;
}


Generated by  Doxygen 1.6.0   Back to index