diff --git a/exit_code.c b/exit_code.c index 993ee01..8d74c33 100644 --- a/exit_code.c +++ b/exit_code.c @@ -17,6 +17,8 @@ * GNU General Public License for more details. */ +#include "sipsak.h" + #if HAVE_CONFIG_H # include "config.h" #endif @@ -34,6 +36,22 @@ enum exit_modes exit_mode = EM_DEFAULT; void exit_code(int code) { +#ifdef WITH_TLS_TRANSP + if (transport == SIP_TLS_TRANSPORT) { +# ifdef USE_GNUTLS + if (tls_session) { + gnutls_deinit(tls_session); + } + if (xcred) { + gnutls_certificate_free_credentials(xcred); + } + gnutls_global_deinit(); +# else /* USE_GNUTLS */ +# ifdef USE_OPENSSL +# endif /* USE_OPENSSL */ +# endif /* USE_GNUTLS */ + } +#endif /* WITH_TLS_TRANSP */ switch(exit_mode) { case EM_DEFAULT: diff --git a/sipsak.c b/sipsak.c index c311ad5..0a42b9f 100644 --- a/sipsak.c +++ b/sipsak.c @@ -91,6 +91,7 @@ void print_version() { ); } +#ifdef HAVE_GETOPT_LONG void print_long_help() { print_version(); printf( @@ -158,6 +159,7 @@ void print_long_help() { ); exit_code(0); } +#endif /* prints out some usage help and exits */ void print_help() { @@ -289,6 +291,9 @@ int main(int argc, char *argv[]) {"transport", 1, 0, 'E'}, {"headers", 1, 0, 'j'}, {"authhash", 1, 0, 'J'}, +#ifdef WITH_TLS_TRANSP + {"tls-ca-cert", 1, 0, 'k'}, +#endif {0, 0, 0, 0} }; #endif @@ -315,11 +320,16 @@ int main(int argc, char *argv[]) memset(buff, 0, BUFSIZE); memset(fqdn, 0, FQDN_SIZE); #ifdef WITH_TLS_TRANSP -# ifdef USE_OPENSSL + cert_file = ca_file = NULL; +# ifdef USE_GNUTLS + tls_session = NULL; + xcred = NULL; +# else +# ifdef USE_OPENSSL ctx = NULL; ssl = NULL; - cert_file = ca_file = NULL; -# endif +# endif +#endif #endif /* WITH_TLS_TRANSP */ if (argc==1) { @@ -500,6 +510,15 @@ int main(int argc, char *argv[]) } authhash=optarg; break; + case 'k': + pf = fopen(optarg, "rb"); + if (!pf){ + fprintf(stderr, "error: unable to open the CA cert file '%s'.\n", optarg); + exit_code(2); + } + fclose(pf); + ca_file=optarg; + break; case 'l': lport=str_to_int(optarg); break; @@ -935,8 +954,10 @@ int main(int argc, char *argv[]) gnutls_global_init(); //gnutls_anon_allocate_client_credentials(&anoncred); gnutls_certificate_allocate_credentials(&xcred); - // set the trusted CA file - gnutls_certificate_set_x509_trust_file(xcred, "ca.pem", GNUTLS_X509_FMT_PEM); + if (ca_file != NULL) { + // set the trusted CA file + gnutls_certificate_set_x509_trust_file(xcred, ca_file, GNUTLS_X509_FMT_PEM); + } # else # ifdef USE_OPENSSL SSL_library_init(); diff --git a/sipsak.h b/sipsak.h index e6d784d..9041105 100644 --- a/sipsak.h +++ b/sipsak.h @@ -300,8 +300,10 @@ // FIXME: this has to replaced with a real evaluation // #define WITH_TLS_TRANSP 1 +// #define DEBUG 1 #ifdef WITH_TLS_TRANSP +char *cert_file, *ca_file; # ifdef USE_GNUTLS gnutls_session_t tls_session; //gnutls_anon_client_credentials_t anoncred; @@ -310,7 +312,6 @@ gnutls_certificate_credentials_t xcred; # ifdef USE_OPENSSL SSL_CTX* ctx; SSL* ssl; -char *cert_file, *ca_file; # endif # endif #endif diff --git a/transport.c b/transport.c index 654eb20..0339544 100644 --- a/transport.c +++ b/transport.c @@ -123,6 +123,231 @@ void check_alert(gnutls_session_t session, int ret) { } } +/* all the available CRLs */ +gnutls_x509_crl_t *crl_list; +int crl_list_size; + +/* all the available trusted CAs */ +gnutls_x509_crt_t *ca_list; +int ca_list_size; + +/* verifies a certificate against an other certificate which is supposed to + * be it's issuer. Also checks the crl_list of the certificate is revoked. + */ +static void verify_cert2(gnutls_x509_crt_t crt, gnutls_x509_crt_t issuer, + gnutls_x509_crl_t *crl_list, int crl_list_size) { + unsigned int output; + time_t now = time(0); + size_t name_size; + char name[64]; + + /* print information about the certificates to be checked */ + name_size = sizeof(name); + gnutls_x509_crt_get_dn(crt, name, &name_size); + + printf("Certificate: %s\n", name); + + name_size = sizeof(name); + gnutls_x509_crt_get_issuer_dn(crt, name, &name_size); + + printf("Issued by: %s\n", name); + + /* Get the DN of the issuer cert. */ + name_size = sizeof(name); + gnutls_x509_crt_get_dn(issuer, name, &name_size); + + printf("Checking against: %s\n", name); + + /* Do the actual verification */ + gnutls_x509_crt_verify(crt, &issuer, 1, 0, &output); + + if (output & GNUTLS_CERT_INVALID) { + printf("Certificate not trusted!!!"); + if (output & GNUTLS_CERT_SIGNER_NOT_FOUND) { + printf(": no issuer was found\n"); + } + if (output & GNUTLS_CERT_SIGNER_NOT_CA) { + printf(": issuer is not a CA\n"); + } + } + else { + printf("Certificate trusted'n"); + } + + /* Now check the expiration dates */ + if (gnutls_x509_crt_get_activation_time(crt) > now) { + printf("Certificate not yet activated!\n"); + } + if (gnutls_x509_crt_get_expiration_time(crt) < now) { + printf("Certificate expired!\n"); + } + /* Check if the certificate is revoked */ + if (gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size) == 1) { + printf("Certificate is revoked!\n"); + } +} + +/* Verifies a certificate against our trusted CA list. Also checks the crl_list + * if the certificate is revoked + */ +static void verify_last_cert(gnutls_x509_crt_t crt, gnutls_x509_crt_t *ca_list, + int ca_list_size, gnutls_x509_crl_t *crl_list, int crl_list_size) { + unsigned int output; + time_t now = time(0); + size_t name_size; + char name[64]; + + /* Print information about the certificates to be checked */ + name_size = sizeof(name); + gnutls_x509_crt_get_dn(crt, name, &name_size); + printf("Certificate: %s\n", name); + + name_size = sizeof(name); + gnutls_x509_crt_get_issuer_dn(crt, name, &name_size); + printf("Issued by: %s\n", name); + + /* Do the actual verification */ + gnutls_x509_crt_verify(crt, ca_list, ca_list_size, + GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT, &output); + if (output & GNUTLS_CERT_INVALID) { + printf("Certificate not truested!\n"); + if (output & GNUTLS_CERT_SIGNER_NOT_CA) { + printf(": Issuer is not a CA\n"); + } + } + else { + printf("Certificate trusted\n"); + } + + /* Now check the expiration dates */ + if (gnutls_x509_crt_get_activation_time(crt) > now) { + printf("Certificate now yet activated!\n"); + } + if (gnutls_x509_crt_get_expiration_time(crt) < now) { + printf("Certificate expired!\n"); + } + /* Check of the vertificate is revoked */ + if (gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size) == 1) { + printf("Certificate is revoked!\n"); + } +} + +/* this function will try yo verify the peer's certificate chain, ans + * also check if the hostname matches, and the activation and expiration dates. + */ +void verify_certificate_chain(gnutls_session_t session, const char *hostname, + const gnutls_datum_t *cert_chain, int cert_chain_length) { + int i; + gnutls_x509_crt_t *cert; + + cert = malloc(sizeof(*cert) * cert_chain_length); + if (!cert) { + printf("gnutla: failed to allocate memory for cert chain verification'n"); + return; + } + + /* import all the certificates in the chain to native certificate format */ + for (i = 0; i < cert_chain_length; i++) { + gnutls_x509_crt_init(&cert[i]); + gnutls_x509_crt_import(cert[i], &cert_chain[i], GNUTLS_X509_FMT_DER); + } + + /* if the last certificate in the chain is seld signed ignore it. + * that is because we want to check against our trusted certificate list + */ + if (gnutls_x509_crt_check_issuer(cert[cert_chain_length - 1], + cert[cert_chain_length -1]) > 0 && cert_chain_length > 0) { + cert_chain_length--; + } + /* now verify the certificates against ther issuers in the chain */ + for (i = 1; i < cert_chain_length; i++) { + verify_cert2(cert[i - 1], cert[i], crl_list, crl_list_size); + } + /* here we must verify the last certificate in the chain against our + * trusted CA list + */ + verify_last_cert(cert[cert_chain_length - 1], ca_list, ca_list_size, + crl_list, crl_list_size); + /* check if the name in the first certificate matches our destination */ + if (!gnutls_x509_crt_check_hostname(cert[0], hostname)) { + printf("The certificate's owner does not match hostname '%s'\n", + hostname); + } + + for (i = 0; i < cert_chain_length; i++) { + gnutls_x509_crt_deinit(cert[i]); + } + return; +} + +void verify_certificate_simple(gnutls_session_t session, const char *hostname) { + unsigned int status, cert_list_size; + const gnutls_datum_t *cert_list; + int ret; + gnutls_x509_crt_t cert; + + // this verification function usese the trusted CAs in the credentials + // stucture. so you must have installed on or more CA certificates. + ret = gnutls_certificate_verify_peers2(session, &status); + + if (ret < 0) { + printf("gnutls verify peer failed\n"); + return; + } + + if (status & GNUTLS_CERT_INVALID) { + printf("The certificate is not trusted\n"); + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { + printf("The certificate hasn't got a known issuer.\n"); + } + if (status & GNUTLS_CERT_SIGNER_NOT_CA) { + printf("The certificate issuer is not a CA\n"); + } + } + if (status & GNUTLS_CERT_REVOKED) { + printf("The certificate has beend revoked.\n"); + } + + // from here on it works only with X509 certs + if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509){ + return; + } + if (gnutls_x509_crt_init(&cert) < 0) { + printf("gnutls crt init failed\n"); + return; + } + + cert_list = gnutls_certificate_get_peers(session, &cert_list_size); + if (cert_list == NULL) { + printf("gnutls did not found a certificate'n"); + return; + } + + // this not a real world check as only the first cert is checked! + if (gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)) { + printf("gnutls failed to parse certificate\n"); + return; + } + + // beware here we do not check for errors + if (gnutls_x509_crt_get_expiration_time(cert) < time(0)) { + printf("The certificate hax expired\n"); + return; + } + if (gnutls_x509_crt_get_activation_time(cert) > time(0)) { + printf("The certificate is not yet activated\n"); + return; + } + if (!gnutls_x509_crt_check_hostname(cert, hostname)) { + printf("The certificate's owner does not match hostname '%s'", + hostname); + return; + } + + gnutls_x509_crt_deinit(cert); + return; +} + static const char *bin2hex(const void *bin, size_t bin_size) { static char printable[110]; const unsigned char *_bin = bin; @@ -149,7 +374,7 @@ void print_x509_certificate_info(gnutls_session_t session) { unsigned int algo, bits; time_t expiration_time, activation_time; const gnutls_datum_t *cert_list; - int cert_list_size = 0; + unsigned int cert_list_size = 0; gnutls_x509_crt_t cert; // check if we got a X.509 cert @@ -920,6 +1145,8 @@ int set_target(struct sockaddr_in *adr, unsigned long target, int port, int sock #ifdef DEBUG gnutls_session_info(tls_session); #endif + verify_certificate_simple(tls_session, domainname); + //verify_certificate_chain(tls_session, domainname, cert_chain, cert_chain_length); } #endif # else /* USE_GNUTLS */