diff --git a/sipsak.c b/sipsak.c index 266749f..71563e2 100644 --- a/sipsak.c +++ b/sipsak.c @@ -163,6 +163,12 @@ void print_long_help() { " --sylog=NUMBER log exit message to syslog with given log level\n" , DEFAULT_TIMEOUT ); +#ifdef WITH_TLS_TRANSP + printf(" --tls-ca-cert=FILE file with the cert of the root CA\n" + " --tls-client-cert=FILE file with the cert which sipsak will send\n" + " --tls-ignore-cert-failure ignore failures during the TLS handshake\n" + ); +#endif exit_code(0, __PRETTY_FUNCTION__, NULL); } #endif @@ -255,7 +261,7 @@ int main(int argc, char *argv[]) #ifdef HAVE_GETOPT_LONG int option_index = 0; static struct option l_opts[] = { - {"help", 0, 0, 'X'}, + {"help", 0, 0, 0}, {"version", 0, 0, 'V'}, {"filename", 1, 0, 'f'}, {"sip-uri", 1, 0, 's'}, @@ -303,7 +309,9 @@ int main(int argc, char *argv[]) {"authhash", 1, 0, 'J'}, {"syslog", 1, 0, 'K'}, #ifdef WITH_TLS_TRANSP - {"tls-ca-cert", 1, 0, 'k'}, + {"tls-ca-cert", 1, 0, 0}, + {"tls-client-cert", 1, 0, 0}, + {"tls-ignore-cert-failure", 0, 0, 0}, #endif {0, 0, 0, 0} }; @@ -334,6 +342,7 @@ int main(int argc, char *argv[]) memset(fqdn, 0, FQDN_SIZE); #ifdef WITH_TLS_TRANSP cert_file = ca_file = NULL; + ignore_ca_fail = 0; # ifdef USE_GNUTLS tls_session = NULL; xcred = NULL; @@ -351,11 +360,49 @@ int main(int argc, char *argv[]) /* lots of command line switches to handle*/ #ifdef HAVE_GETOPT_LONG - while ((c=getopt_long(argc, argv, "a:A:b:B:c:C:dD:e:E:f:Fg:GhH:iIj:J:k:K:l:Lm:MnNo:O:p:P:q:r:Rs:St:Tu:UvVwW:x:Xz:Z:", l_opts, &option_index)) != EOF){ + while ((c=getopt_long(argc, argv, "a:A:b:B:c:C:dD:e:E:f:Fg:GhH:iIj:J:K:l:Lm:MnNo:O:p:P:q:r:Rs:St:Tu:UvVwW:x:z:Z:", l_opts, &option_index)) != EOF){ #else - while ((c=getopt(argc, argv, "a:A:b:B:c:C:dD:e:E:f:Fg:GhH:iIj:J:k:K:l:Lm:MnNo:O:p:P:q:r:Rs:St:Tu:UvVwW:x:z:Z:")) != EOF){ + while ((c=getopt(argc, argv, "a:A:b:B:c:C:dD:e:E:f:Fg:GhH:iIj:J:K:l:Lm:MnNo:O:p:P:q:r:Rs:St:Tu:UvVwW:x:z:Z:")) != EOF){ #endif switch(c){ +#ifdef HAVE_GETOPT_LONG + case 0: + printf("long option %s", l_opts[option_index].name); + if (optarg) { + printf(" with arg %s", optarg); + } + printf("\n"); + if (STRNCASECMP("help", l_opts[option_index].name, 4) == 0) { + print_long_help(); + } +#ifdef WITH_TLS_TRANSP + else if (STRNCASECMP("tls-ca-cert", l_opts[option_index].name, 11) == 0) { + pf = fopen(optarg, "rb"); + if (!pf){ + fprintf(stderr, "error: unable to open the CA cert file '%s'.\n", optarg); + exit_code(2, __PRETTY_FUNCTION__, "failed to open CA cert file"); + } + fclose(pf); + ca_file=optarg; + break; + } + else if (STRNCASECMP("tls-ignore-cert-failure", l_opts[option_index].name, 22) == 0) { + ignore_ca_fail = 1; + break; + } + else if (STRNCASECMP("tls-client-cert", l_opts[option_index].name, 14) == 0) { + pf = fopen(optarg, "rb"); + if (!pf){ + fprintf(stderr, "error: unable to open the client cert file '%s'.\n", optarg); + exit_code(2, __PRETTY_FUNCTION__, "failed to open client cert file"); + } + fclose(pf); + cert_file=optarg; + break; + } +#endif + break; +#endif case 'a': if (strlen(optarg) == 1 && STRNCASECMP(optarg, "-", 1) == 0) { password = str_alloc(SIPSAK_MAX_PASSWD_LEN); @@ -528,17 +575,6 @@ int main(int argc, char *argv[]) } authhash=optarg; break; -#ifdef WITH_TLS_TRANSP - case 'k': - pf = fopen(optarg, "rb"); - if (!pf){ - fprintf(stderr, "error: unable to open the CA cert file '%s'.\n", optarg); - exit_code(2, __PRETTY_FUNCTION__, "failed to open CA cert file"); - } - fclose(pf); - ca_file=optarg; - break; -#endif case 'K': sysl=str_to_int(0, optarg); if (sysl < LOG_ALERT || sysl > LOG_DEBUG) { @@ -786,11 +822,6 @@ int main(int argc, char *argv[]) case 'x': expires_t=str_to_int(0, optarg); break; -#ifdef HAVE_GETOPT_LONG - case 'X': - print_long_help(); - break; -#endif case 'z': rand_rem=str_to_int(0, optarg); if (rand_rem < 0 || rand_rem > 100) { @@ -806,7 +837,7 @@ int main(int argc, char *argv[]) } break; default: - fprintf(stderr, "error: unknown parameter %c\n", c); + fprintf(stderr, "error: unknown parameter '%c'\n", c); exit_code(2, __PRETTY_FUNCTION__, "unknown parameter"); break; } diff --git a/sipsak.h b/sipsak.h index a44bf43..c4580be 100644 --- a/sipsak.h +++ b/sipsak.h @@ -306,6 +306,7 @@ #ifdef WITH_TLS_TRANSP char *cert_file, *ca_file; +int ignore_ca_fail; # ifdef USE_GNUTLS gnutls_session_t tls_session; //gnutls_anon_client_credentials_t anoncred; diff --git a/transport.c b/transport.c index 9eb7562..ae35d33 100644 --- a/transport.c +++ b/transport.c @@ -280,7 +280,7 @@ void verify_certificate_chain(gnutls_session_t session, const char *hostname, return; } -void verify_certificate_simple(gnutls_session_t session, const char *hostname) { +int verify_certificate_simple(gnutls_session_t session, const char *hostname) { unsigned int status, cert_list_size; const gnutls_datum_t *cert_list; int ret; @@ -291,61 +291,71 @@ void verify_certificate_simple(gnutls_session_t session, const char *hostname) { ret = gnutls_certificate_verify_peers2(session, &status); if (ret < 0) { - printf("gnutls verify peer failed\n"); - return; + printf("gnutls verify peer failed.\n"); + return -1; } + ret = 0; if (status & GNUTLS_CERT_INVALID) { - printf("The certificate is not trusted\n"); + ret |= -2; + printf("The certificate is not trustworthy\n"); if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { printf("The certificate hasn't got a known issuer.\n"); + ret |= -4; } if (status & GNUTLS_CERT_SIGNER_NOT_CA) { printf("The certificate issuer is not a CA\n"); + ret |= -8; } } if (status & GNUTLS_CERT_REVOKED) { printf("The certificate has beend revoked.\n"); + ret = -16; + } + if (ret != 0 && ignore_ca_fail == 0) { + return ret; } // from here on it works only with X509 certs if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509){ - return; + printf("The server certificate is not X509.\n"); + return -32;; } if (gnutls_x509_crt_init(&cert) < 0) { - printf("gnutls crt init failed\n"); - return; + printf("gnutls crt init failed.\n"); + return -64; } cert_list = gnutls_certificate_get_peers(session, &cert_list_size); if (cert_list == NULL) { - printf("gnutls did not found a certificate'n"); - return; + printf("gnutls did not found a server certificate.\n"); + return -128; } // 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; + printf("gnutls failed to parse server certificate.\n"); + return -256; } // 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; + printf("The server certificate is expired.\n"); + return -512; } if (gnutls_x509_crt_get_activation_time(cert) > time(0)) { - printf("The certificate is not yet activated\n"); - return; + printf("The server certificate is not yet activated.\n"); + return -1024; } if (!gnutls_x509_crt_check_hostname(cert, hostname)) { - printf("The certificate's owner does not match hostname '%s'", + printf("The server certificate's owner does not match hostname '%s'\n", hostname); - return; + return -2048; } gnutls_x509_crt_deinit(cert); - return; + + return ret; } static const char *bin2hex(const void *bin, size_t bin_size) { @@ -371,7 +381,7 @@ void print_x509_certificate_info(gnutls_session_t session) { char serial[40]; char dn[128]; size_t size; - unsigned int algo, bits; + unsigned int algo, bits, sigalgo; time_t expiration_time, activation_time; const gnutls_datum_t *cert_list; unsigned int cert_list_size = 0; @@ -391,9 +401,9 @@ void print_x509_certificate_info(gnutls_session_t session) { gnutls_x509_crt_init(&cert); gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER); printf("Certificate info:\n"); - expiration_time = gnutls_x509_crt_get_expiration_time(cert); activation_time = gnutls_x509_crt_get_activation_time(cert); printf("\tCertificate is valid since: %s", ctime(&activation_time)); + expiration_time = gnutls_x509_crt_get_expiration_time(cert); printf("\tCertificate expires: %s", ctime(&expiration_time)); // print the serial number of the certificate size = sizeof(serial); @@ -401,15 +411,38 @@ void print_x509_certificate_info(gnutls_session_t session) { printf("\tCertificate serail number: %s\n", bin2hex(serial, size)); // extract public key algorithm algo = gnutls_x509_crt_get_pk_algorithm(cert, &bits); - printf("\tCertificate public key: %s\n", gnutls_pk_algorithm_get_name(algo)); + printf("\tCertificate public key algorithm: %s\n", gnutls_pk_algorithm_get_name(algo)); // print version of x509 cert printf("\tCertificate version: #%d\n", gnutls_x509_crt_get_version(cert)); + // print name of the certificate size = sizeof(dn); gnutls_x509_crt_get_dn(cert, dn, &size); printf("\tDN: %s\n", dn); + // print subject alt name of the certificate + size = sizeof(dn); + if (gnutls_x509_crt_get_subject_alt_name(cert, 0, dn, &size, NULL) == 0) { + printf("\tSubject Alt Name: %s\n", dn); + } + // print the algorithm which was used for signing the cert + algo = gnutls_x509_crt_get_signature_algorithm(cert); + printf("\tCA's signature algorithm: %s\n", gnutls_pk_algorithm_get_name(algo)); + // print the name of the CA size = sizeof(dn); - gnutls_x509_crt_get_issuer_dn(cert, dn, &size); - printf("\tIssuer's DN: %s\n", dn); + if (gnutls_x509_crt_get_issuer_dn(cert, dn, &size) == 0) { + printf("\tCA's DN: %s\n", dn); + } + // print the CA status flags if present + if (gnutls_x509_crt_get_ca_status(cert, &algo) > 0 && algo != 0) { + printf("\tCA status flag is set\n"); + } + // print the fingerprint of the cert + size = sizeof(dn); + // FIXME + if (gnutls_x509_crt_get_fingerprint(cert, GNUTLS_MAC_SHA1, dn, &size) == 0) { + printf("\tFingerprint of the certificate: %s\n", dn); + } + + gnutls_x509_crt_deinit(cert); } } @@ -1130,7 +1163,18 @@ int set_target(struct sockaddr_in *adr, unsigned long target, int port, int sock else if (verbose > 2) { dbg(" TLS Handshake was completed!\n"); gnutls_session_info(tls_session); - verify_certificate_simple(tls_session, domainname); + if (verify_certificate_simple(tls_session, domainname) != 0) { + if (ignore_ca_fail == 1) { + if (verbose) { + printf("WARN: Ignoring verification failures of the server certificate\n"); + } + } else { + if (verbose > 1) { + printf("TLS server certificate verification can be ignored with option --tls-ignore-cert-failure.\n"); + } + exit_code(3, __PRETTY_FUNCTION__, "failure during TLS server certificate verification"); + } + } //verify_certificate_chain(tls_session, domainname, cert_chain, cert_chain_length); } # else /* USE_GNUTLS */