📄 mutt_ssl_gnutls.c
字号:
regmatch_t pmatch[3]; /* try checking against names stored in stored certs file */ if ((fp = fopen (SslCertFile, "r"))) { if (regcomp(&preg, "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$", REG_ICASE|REG_EXTENDED) != 0) { regfree(&preg); return 0; } buf[0] = '\0'; tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert); while ((linestr = mutt_read_line(linestr, &linestrsize, fp, &linenum)) != NULL) { if(linestr[0] == '#' && linestr[1] == 'H') { if (regexec(&preg, linestr, 3, pmatch, 0) == 0) { linestr[pmatch[1].rm_eo] = '\0'; linestr[pmatch[2].rm_eo] = '\0'; if (strcmp(linestr + pmatch[1].rm_so, hostname) == 0 && strcmp(linestr + pmatch[2].rm_so, buf) == 0) { regfree(&preg); FREE(&linestr); fclose(fp); return 1; } } } } regfree(&preg); fclose(fp); } /* not found a matching name */ return 0;}static int tls_check_certificate (CONNECTION* conn){ tlssockdata *data = conn->sockdata; gnutls_session state = data->state; char helpstr[SHORT_STRING]; char buf[SHORT_STRING]; char fpbuf[SHORT_STRING]; size_t buflen; char dn_common_name[SHORT_STRING]; char dn_email[SHORT_STRING]; char dn_organization[SHORT_STRING]; char dn_organizational_unit[SHORT_STRING]; char dn_locality[SHORT_STRING]; char dn_province[SHORT_STRING]; char dn_country[SHORT_STRING]; MUTTMENU *menu; int done, row, i, ret; FILE *fp; time_t t; const gnutls_datum *cert_list; unsigned int cert_list_size = 0; gnutls_certificate_status certstat; char datestr[30]; gnutls_x509_crt cert; gnutls_datum pemdata; int certerr_expired = 0; int certerr_notyetvalid = 0; int certerr_hostname = 0; int certerr_nottrusted = 0; int certerr_revoked = 0; int certerr_signernotca = 0; if (gnutls_auth_get_type(state) != GNUTLS_CRD_CERTIFICATE) { mutt_error (_("Unable to get certificate from peer")); mutt_sleep (2); return 0; } certstat = gnutls_certificate_verify_peers(state); if (certstat == GNUTLS_E_NO_CERTIFICATE_FOUND) { mutt_error (_("Unable to get certificate from peer")); mutt_sleep (2); return 0; } if (certstat < 0) { mutt_error (_("Certificate verification error (%s)"), gnutls_strerror(certstat)); mutt_sleep (2); return 0; } /* We only support X.509 certificates (not OpenPGP) at the moment */ if (gnutls_certificate_type_get(state) != GNUTLS_CRT_X509) { mutt_error (_("Certificate is not X.509")); mutt_sleep (2); return 0; } if (gnutls_x509_crt_init(&cert) < 0) { mutt_error (_("Error initialising gnutls certificate data")); mutt_sleep (2); return 0; } cert_list = gnutls_certificate_get_peers(state, &cert_list_size); if (!cert_list) { mutt_error (_("Unable to get certificate from peer")); mutt_sleep (2); return 0; } /* FIXME: Currently only check first certificate in chain. */ if (gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) { mutt_error (_("Error processing certificate data")); mutt_sleep (2); return 0; } if (gnutls_x509_crt_get_expiration_time(cert) < time(NULL)) { certerr_expired = 1; } if (gnutls_x509_crt_get_activation_time(cert) > time(NULL)) { certerr_notyetvalid = 1; } if (!gnutls_x509_crt_check_hostname(cert, conn->account.host) && !tls_check_stored_hostname (&cert_list[0], conn->account.host)) { certerr_hostname = 1; } /* see whether certificate is in our cache (certificates file) */ if (tls_compare_certificates (&cert_list[0])) { if (certstat & GNUTLS_CERT_INVALID) { /* doesn't matter - have decided is valid because server certificate is in our trusted cache */ certstat ^= GNUTLS_CERT_INVALID; } if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) { /* doesn't matter that we haven't found the signer, since certificate is in our trusted cache */ certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND; } if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) { /* Hmm. Not really sure how to handle this, but let's say that we don't care if the CA certificate hasn't got the correct X.509 basic constraints if server certificate is in our cache. */ certstat ^= GNUTLS_CERT_SIGNER_NOT_CA; } } if (certstat & GNUTLS_CERT_REVOKED) { certerr_revoked = 1; certstat ^= GNUTLS_CERT_REVOKED; } if (certstat & GNUTLS_CERT_INVALID) { certerr_nottrusted = 1; certstat ^= GNUTLS_CERT_INVALID; } if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) { /* NB: already cleared if cert in cache */ certerr_nottrusted = 1; certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND; } if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) { /* NB: already cleared if cert in cache */ certerr_signernotca = 1; certstat ^= GNUTLS_CERT_SIGNER_NOT_CA; } /* OK if signed by (or is) a trusted certificate */ /* we've been zeroing the interesting bits in certstat - don't return OK if there are any unhandled bits we don't understand */ if (!(certerr_expired || certerr_notyetvalid || certerr_hostname || certerr_nottrusted) && certstat == 0) { gnutls_x509_crt_deinit(cert); return 1; } /* interactive check from user */ menu = mutt_new_menu (); menu->max = 25; menu->dialog = (char **) safe_calloc (1, menu->max * sizeof (char *)); for (i = 0; i < menu->max; i++) menu->dialog[i] = (char *) safe_calloc (1, SHORT_STRING * sizeof (char)); row = 0; strfcpy (menu->dialog[row], _("This certificate belongs to:"), SHORT_STRING); row++; buflen = sizeof(dn_common_name); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, &buflen) != 0) dn_common_name[0] = '\0'; buflen = sizeof(dn_email); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0) dn_email[0] = '\0'; buflen = sizeof(dn_organization); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization, &buflen) != 0) dn_organization[0] = '\0'; buflen = sizeof(dn_organizational_unit); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0, dn_organizational_unit, &buflen) != 0) dn_organizational_unit[0] = '\0'; buflen = sizeof(dn_locality); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0) dn_locality[0] = '\0'; buflen = sizeof(dn_province); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province, &buflen) != 0) dn_province[0] = '\0'; buflen = sizeof(dn_country); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, &buflen) != 0) dn_country[0] = '\0'; snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name, dn_email); snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization); snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organizational_unit); snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality, dn_province, dn_country); row++; strfcpy (menu->dialog[row], _("This certificate was issued by:"), SHORT_STRING); row++; buflen = sizeof(dn_common_name); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, &buflen) != 0) dn_common_name[0] = '\0'; buflen = sizeof(dn_email); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0) dn_email[0] = '\0'; buflen = sizeof(dn_organization); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization, &buflen) != 0) dn_organization[0] = '\0'; buflen = sizeof(dn_organizational_unit); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0, dn_organizational_unit, &buflen) != 0) dn_organizational_unit[0] = '\0'; buflen = sizeof(dn_locality); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0) dn_locality[0] = '\0'; buflen = sizeof(dn_province); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province, &buflen) != 0) dn_province[0] = '\0'; buflen = sizeof(dn_country); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, &buflen) != 0) dn_country[0] = '\0'; snprintf (menu->dialog[row++], SHORT_STRING, " %s %s", dn_common_name, dn_email); snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organization); snprintf (menu->dialog[row++], SHORT_STRING, " %s", dn_organizational_unit); snprintf (menu->dialog[row++], SHORT_STRING, " %s %s %s", dn_locality, dn_province, dn_country); row++; snprintf (menu->dialog[row++], SHORT_STRING, _("This certificate is valid")); t = gnutls_x509_crt_get_activation_time(cert); snprintf (menu->dialog[row++], SHORT_STRING, _(" from %s"), tls_make_date(t, datestr, 30)); t = gnutls_x509_crt_get_expiration_time(cert); snprintf (menu->dialog[row++], SHORT_STRING, _(" to %s"), tls_make_date(t, datestr, 30)); fpbuf[0] = '\0'; tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), &cert_list[0]); snprintf (menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"), fpbuf); fpbuf[0] = '\0'; tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), &cert_list[0]); snprintf (menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"), fpbuf); if (certerr_notyetvalid) { row++; strfcpy (menu->dialog[row], _("WARNING: Server certificate is not yet valid"), SHORT_STRING); } if (certerr_expired) { row++; strfcpy (menu->dialog[row], _("WARNING: Server certificate has expired"), SHORT_STRING); } if (certerr_revoked) { row++; strfcpy (menu->dialog[row], _("WARNING: Server certificate has been revoked"), SHORT_STRING); } if (certerr_hostname) { row++; strfcpy (menu->dialog[row], _("WARNING: Server hostname does not match certificate"), SHORT_STRING); } if (certerr_signernotca) { row++; strfcpy (menu->dialog[row], _("WARNING: Signer of server certificate is not a CA"), SHORT_STRING); } menu->title = _("TLS/SSL Certificate check"); /* certificates with bad dates, or that are revoked, must be accepted manually each and every time */ if (SslCertFile && !certerr_expired && !certerr_notyetvalid && !certerr_revoked) { menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always"); menu->keys = _("roa"); } else { menu->prompt = _("(r)eject, accept (o)nce"); menu->keys = _("ro"); } helpstr[0] = '\0'; mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_GENERIC, OP_EXIT); safe_strcat (helpstr, sizeof (helpstr), buf); mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP); safe_strcat (helpstr, sizeof (helpstr), buf); menu->help = helpstr; done = 0; set_option (OPTUNBUFFEREDINPUT); while (!done) { switch (mutt_menuLoop (menu)) { case -1: /* abort */ case OP_MAX + 1: /* reject */ case OP_EXIT: done = 1; break; case OP_MAX + 3: /* accept always */ done = 0; if ((fp = fopen (SslCertFile, "a"))) { /* save hostname if necessary */ if (certerr_hostname) { fprintf(fp, "#H %s %s\n", conn->account.host, fpbuf); done = 1; } if (certerr_nottrusted) { done = 0; ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", &cert_list[0], &pemdata); if (ret == 0) { if (fwrite(pemdata.data, pemdata.size, 1, fp) == 1) { done = 1; } gnutls_free(pemdata.data); } } fclose (fp); } if (!done) { mutt_error (_("Warning: Couldn't save certificate")); mutt_sleep (2); } else { mutt_message (_("Certificate saved")); mutt_sleep (0); } /* fall through */ case OP_MAX + 2: /* accept once */ done = 2; break; } } unset_option (OPTUNBUFFEREDINPUT); mutt_menuDestroy (&menu); gnutls_x509_crt_deinit(cert); return (done == 2);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -