Hi,
currently, I'm writing code to implement GnuTLS to the POP-protocol.
The following code enables me to make a encrypted connection to a POP
server:
| #include <stdio.h>
| #include <stdlib.h>
| #include <sys/socket.h> // AF_INET, SOCK_STREAM, connect()
| #include <netdb.h> // struct hostent
| #include <netinet/in.h> // struct sockadd_in
| #include <gnutls/gnutls.h>
|
| #define MAX_BUF 1024
| #define MAXMSG 512
| #define CAFILE "/etc/ssl/certs/ca-certificates.crt"
|
| struct hostent *hostinfo;
| struct sockaddr_in name;
|
| void IP_number(struct sockaddr_in *name, char *servername, uint16_t port)
| {
| char serverIP[16];
| if(hostinfo=gethostbyname(servername))
| {
| // converting name in IP number
| inet_ntop(PF_INET,hostinfo->h_addr_list[0],serverIP,16);
| printf("IP: %s\n",serverIP);
| name->sin_family = PF_INET;
| name->sin_port = htons(port);
| name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
| }
| else
| {
| fprintf(stderr, "Unknown host %s.\n",servername);
| exit (EXIT_FAILURE);
| }
| }
|
| main()
| {
| int ret, sd, ii;
|
| gnutls_session_t session;
| gnutls_srp_client_credentials_t srp_cred;
| gnutls_certificate_credentials_t cert_cred;
|
| char buffer2[MAX_BUF + 1];
|
| // Required data
| char server[30];
| printf("Server: ");
| scanf("%s",server);
|
| char USER[30]="";
| printf("User: ");
| scanf("%s",USER);
|
| char *password;
| password=(char *)getpass("Password: ");
|
| char buffer[MAXMSG];
| uint16_t port=110;
| int sock=-1;
| size_t size;
|
| // converting server name in IP number
| IP_number(&name, server, port);
|
| // Creating socket for connection to this server
| sock = socket(AF_INET, SOCK_STREAM, 0);
| if (sock == -1)
| {
| perror("socket");
| exit (EXIT_FAILURE);
| }
|
| // Making the connection
| if (connect(sock, (struct sockaddr *) &name, sizeof (struct sockaddr_in)) < 0)
| {
| perror ("connect");
| exit (EXIT_FAILURE);
| }
|
| // Reading
| int amount = read(sock, buffer, MAXMSG);
| printf("%.*s", amount, buffer);
| write(sock,"CAPA\r\n",6);
| amount = read(sock, buffer, MAXMSG);
| printf("%.*s", amount, buffer);
| write(sock,"STLS\r\n",6);
| amount = read(sock, buffer, MAXMSG);
| printf("%.*s", amount, buffer);
|
| // Security connection
|
| gnutls_global_init();
|
| gnutls_srp_allocate_client_credentials (&srp_cred);
| gnutls_certificate_allocate_credentials (&cert_cred);
| gnutls_certificate_set_x509_trust_file (cert_cred, CAFILE, GNUTLS_X509_FMT_PEM);
| gnutls_srp_set_client_credentials (srp_cred, USER, password);
|
| gnutls_init (&session, GNUTLS_CLIENT);
|
| gnutls_priority_set_direct (session, "NORMAL:+SRP:+SRP-RSA:+SRP-DSS", NULL);
|
| gnutls_credentials_set (session, GNUTLS_CRD_SRP, srp_cred);
| gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, cert_cred);
| gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sock);
|
| do
| {
| ret = gnutls_handshake (session);
| } while (ret < 00 && gnutls_error_is_fatal (ret) == 0);
|
| if (ret < 0)
| {
| fprintf (stderr, "*** Handshake failed\n");
| gnutls_perror (ret);
| goto end;
| }
| else
| {
| printf ("- Handshake was completed\n");
| }
|
| gnutls_record_send(session, "USER ########\r\n",15); ret =
| gnutls_record_recv(session,buffer2,MAX_BUF);
| if (gnutls_error_is_fatal (ret) != 0 || ret == 0)
| {
| if (ret == 0)
| {
| printf ("- Peer has closed the GnuTLS connection\n");
| goto end;
| }
| else
| {
| fprintf (stderr, "*** Error: %s\n", gnutls_strerror (ret));
| goto end;
| }
| }
|
| if (ret > 0)
| {
| printf ("- Reveived %d bytes: ", ret);
| for (ii = 0 ; ii < ret; ii++)
| {
| fputc (buffer2[ii], stdout);
| }
| fputs ("\n", stdout);
| }
|
| gnutls_record_send(session, "PASS ########\r\n",15); ret =
| gnutls_record_recv(session,buffer2,MAX_BUF);
| if (gnutls_error_is_fatal (ret) != 0 || ret == 0)
| {
| if (ret == 0)
| {
| printf ("- Peer has closed the GnuTLS connection\n");
| goto end;
| }
| else
| {
| fprintf (stderr, "*** Error: %s\n", gnutls_strerror (ret));
| goto end;
| }
| }
|
| if (ret > 0)
| {
| printf ("- Reveived %d bytes: ", ret);
| for (ii = 0 ; ii < ret; ii++)
| {
| fputc (buffer2[ii], stdout);
| }
| fputs ("\n", stdout);
| }
|
| gnutls_record_send(session, "RETR 1\r\n",8);
| ret = gnutls_record_recv(session,buffer2,MAX_BUF);
| if (gnutls_error_is_fatal (ret) != 0 || ret == 0)
| {
| if (ret == 0)
| {
| printf ("- Peer has closed the GnuTLS connection\n");
| goto end;
| }
| else
| {
| fprintf (stderr, "*** Error: %s\n", gnutls_strerror (ret));
| goto end;
| }
| }
|
| if (ret > 0)
| {
| printf ("- Reveived %d bytes: ", ret);
| for (ii = 0 ; ii < ret; ii++)
| {
| fputc (buffer2[ii], stdout);
| }
| fputs ("\n", stdout);
| }
|
|
| gnutls_bye (session, GNUTLS_SHUT_RDWR);
|
| end:
|
| // Closing socket
| close(sock);
|
| gnutls_deinit(session);
|
| gnutls_srp_free_client_credentials (srp_cred);
| gnutls_certificate_free_credentials (cert_cred);
|
| gnutls_global_deinit();
|
| printf("Ready\n");
|
| }
I replaced two sensible data by ########.
The programme works but doesn't check the server certificate. In Cygwin,
the certificate trust list can be found at
#define CAFILE "/usr/ssl/certs/ca-bundle.trust.crt"
which isn't used in the previous programme. So I extended the previous
programme to
| #ifdef HAVE_CONFIG_H
| #include <config.h>
| #endif
|
| #include <stdio.h>
| #include <stdlib.h>
| #include <sys/socket.h> // AF_INET, SOCK_STREAM, connect()
| #include <netdb.h> // struct hostent
| #include <netinet/in.h> // struct sockadd_in
| #include <gnutls/gnutls.h>
| #include <gnutls/x509.h>
| #include <string.h>
|
| #define MAX_BUF 1024
| #define MAXMSG 512
| #define CAFILE "/usr/ssl/certs/ca-bundle.trust.crt"
|
| struct hostent *hostinfo;
| struct sockaddr_in name;
|
| static int _verify_certificate_callback (gnutls_session_t session);
|
| void IP_number(struct sockaddr_in *name, char *servername, uint16_t port)
| {
| char serverIP[16];
| if(hostinfo=gethostbyname(servername))
| {
| // converting name in IP number
| inet_ntop(PF_INET,hostinfo->h_addr_list[0],serverIP,16);
| printf("IP: %s\n",serverIP);
| name->sin_family = PF_INET;
| name->sin_port = htons(port);
| name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
| }
| else
| {
| fprintf(stderr, "Unknown host %s.\n",servername);
| exit (EXIT_FAILURE);
| }
| }
|
| main()
| {
| int ret, sd, ii;
|
| gnutls_session_t session;
| gnutls_srp_client_credentials_t srp_cred;
| gnutls_certificate_credentials_t cert_cred;
|
| char buffer2[MAX_BUF + 1];
| const char *err;
|
| // Required data
| char server[30];
| printf("Server: ");
| scanf("%s",server);
|
| char USER[30]="";
| printf("User: ");
| scanf("%s",USER);
|
| char *password;
| password=(char *)getpass("Password: ");
|
| char buffer[MAXMSG];
| uint16_t port=110;
| int sock=-1;
| size_t size;
|
| // converting server name in IP number
| IP_number(&name, server, port);
|
| // Creating socket for connection to this server
| sock = socket(AF_INET, SOCK_STREAM, 0);
| if (sock == -1)
| {
| perror("socket");
| exit (EXIT_FAILURE);
| }
|
| // Making the connection
| if (connect(sock, (struct sockaddr *) &name, sizeof (struct sockaddr_in)) < 0)
| {
| perror ("connect");
| exit (EXIT_FAILURE);
| }
|
| // Reading
| int amount = read(sock, buffer, MAXMSG);
| printf("%.*s", amount, buffer);
| write(sock,"CAPA\r\n",6);
| amount = read(sock, buffer, MAXMSG);
| printf("%.*s", amount, buffer);
| write(sock,"STLS\r\n",6);
| amount = read(sock, buffer, MAXMSG);
| printf("%.*s", amount, buffer);
|
| // Security connection
|
| gnutls_global_init();
|
| gnutls_srp_allocate_client_credentials (&srp_cred);
| gnutls_certificate_allocate_credentials (&cert_cred);
| printf("\nNumber of processed certificates: %d\n",
gnutls_certificate_set_x509_trust_file (cert_cred, CAFILE, GNUTLS_X509_FMT_PEM));
| gnutls_certificate_set_verify_function (cert_cred, _verify_certificate_callback);
| gnutls_srp_set_client_credentials (srp_cred, USER, password);
|
| gnutls_init (&session, GNUTLS_CLIENT);
|
| gnutls_session_set_ptr (session, (void *) server);
| gnutls_server_name_set (session, GNUTLS_NAME_DNS, server, strlen(server));
|
| /* Use default priorities */
| ret = gnutls_priority_set_direct (session, "NORMAL:+SRP:+SRP-RSA:+SRP-DSS", &err);
| if (ret < 0)
| {
| if (ret == GNUTLS_E_INVALID_REQUEST)
| {
| fprintf (stderr, "Syntax error at: %s\n", err);
| }
| exit(1);
| }
|
| gnutls_credentials_set (session, GNUTLS_CRD_SRP, srp_cred);
| gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, cert_cred);
| gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sock);
|
| do
| {
| ret = gnutls_handshake (session);
| } while (ret < 00 && gnutls_error_is_fatal (ret) == 0);
|
| if (ret < 0)
| {
| fprintf (stderr, "*** Handshake failed\n");
| gnutls_perror (ret);
| goto end;
| }
| else
| {
| printf ("- Handshake was completed\n");
| }
|
| gnutls_record_send(session, "USER ########\r\n",15);
| ret = gnutls_record_recv(session,buffer2,MAX_BUF);
| if (gnutls_error_is_fatal (ret) != 0 || ret == 0)
| {
| if (ret == 0)
| {
| printf ("- Peer has closed the GnuTLS connection\n");
| goto end;
| }
| else
| {
| fprintf (stderr, "*** Error: %s\n", gnutls_strerror (ret));
| goto end;
| }
| }
|
| if (ret > 0)
| {
| printf ("- Reveived %d bytes: ", ret);
| for (ii = 0 ; ii < ret; ii++)
| {
| fputc (buffer2[ii], stdout);
| }
| fputs ("\n", stdout);
| }
|
| gnutls_record_send(session, "PASS ########\r\n",15);
| ret = gnutls_record_recv(session,buffer2,MAX_BUF);
| if (gnutls_error_is_fatal (ret) != 0 || ret == 0)
| {
| if (ret == 0)
| {
| printf ("- Peer has closed the GnuTLS connection\n");
| goto end;
| }
| else
| {
| fprintf (stderr, "*** Error: %s\n", gnutls_strerror (ret));
| goto end;
| }
| }
|
| if (ret > 0)
| {
| printf ("- Reveived %d bytes: ", ret);
| for (ii = 0 ; ii < ret; ii++)
| {
| fputc (buffer2[ii], stdout);
| }
| fputs ("\n", stdout);
| }
|
| gnutls_record_send(session, "RETR 1\r\n",8);
| ret = gnutls_record_recv(session,buffer2,MAX_BUF);
| if (gnutls_error_is_fatal (ret) != 0 || ret == 0)
| {
| if (ret == 0)
| {
| printf ("- Peer has closed the GnuTLS connection\n");
| goto end;
| }
| else
| {
| fprintf (stderr, "*** Error: %s\n", gnutls_strerror (ret));
| goto end;
| }
| }
|
| if (ret > 0)
| {
| printf ("- Reveived %d bytes: ", ret);
| for (ii = 0 ; ii < ret; ii++)
| {
| fputc (buffer2[ii], stdout);
| }
| fputs ("\n", stdout);
| }
|
|
| gnutls_bye (session, GNUTLS_SHUT_RDWR);
|
| end:
|
| // Closing socket
| close(sock);
|
| gnutls_deinit(session);
|
| gnutls_srp_free_client_credentials (srp_cred);
| gnutls_certificate_free_credentials (cert_cred);
|
| gnutls_global_deinit();
|
| printf("Ready\n");
|
| }
|
| static int _verify_certificate_callback (gnutls_session_t session)
| {
| unsigned int status;
| const gnutls_datum_t *cert_list;
| unsigned int cert_list_size;
| int ret;
| gnutls_x509_crt_t cert;
| const char *hostname;
|
| /* read hostname */
| hostname = gnutls_session_get_ptr (session);
|
| /* This varification function uses the trusted CAs in the credentials
| * structure. So you must have installed one or more CA certificates.
| */
|
| ret = gnutls_certificate_verify_peers2 (session, &status);
| if (ret < 0)
| {
| printf ("Error\n");
| return GNUTLS_E_CERTIFICATE_ERROR;
| }
|
| if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
| printf ("The certificate hasn't got a known issuer\n");
|
| if (status & GNUTLS_CERT_REVOKED)
| printf ("The certificate has been revoked\n");
|
| if (status & GNUTLS_CERT_EXPIRED)
| printf ("The certificate has expired\n");
|
| if (status & GNUTLS_CERT_NOT_ACTIVATED)
| printf ("The certificate is not yet activated\n");
|
| if (status & GNUTLS_CERT_INVALID)
| {
| printf ("The certificate is not trusted\n");
| return GNUTLS_E_CERTIFICATE_ERROR;
| }
|
| /* Up to here the process is the same for X.509 certificates and
| * OpenPGP keys. From now on X.509 certificates are assumed. This can
| * be easily extended to work with openpgp keys as well.
| */
|
| if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509)
| return GNUTLS_E_CERTIFICATE_ERROR;
|
| if (gnutls_x509_crt_init (&cert) < 0 )
| {
| printf ("error in initialization\n");
| return GNUTLS_E_CERTIFICATE_ERROR;
| }
|
| cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
| if (cert_list == NULL)
| {
| printf ("No certificate was found!\n");
| return GNUTLS_E_CERTIFICATE_ERROR;
| }
|
| if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
| {
| printf ("error parsing certificate\n");
| return GNUTLS_E_CERTIFICATE_ERROR;
| }
|
| if (!gnutls_x509_crt_check_hostname (cert, hostname))
| {
| printf ("The certificate's owner does not match hostname '%s'\n", hostname);
| return GNUTLS_E_CERTIFICATE_ERROR;
| }
|
| gnutls_x509_crt_deinit (cert);
|
| /* notify gnutls to continue handshake normally */
| return 0;
| }
(Again: ######## replaces some sensible data - I have to work at this
place).
To make a secure connection I have problems with the certificates. The
function
gnutls_certificate_set_x509_trust_file (cert_cred, CAFILE,
GNUTLS_X509_FMT_PEM)
always returns zero (= number of processed certificates). It seems to me
that it isn't able to process any of the certificates given in the list
/usr/ssl/certs/ca-bundle.trust.crt
of Cygwin. I tried to attach the required certificats for a special
connection from the belonging URLs, but the in the web given
certificates are different to the ones in the named file
ca-bundle.trust.de.
One example is:
URL:
http://www.telesec.de/downloads/DT-Root-CA-2.cer
The website says it is in PEM-format. The by Cygwin given certificate
list contains also a certificate, called Deutsche Telekom Root CA 2 but
it differs from the upper certificate and begins with
Certificate:
Data:
Version: 3 (0x2)
I suppose the certificates in the file
/usr/ssl/certs/ca-bundle.trust.crt are in x.509 format because
of
http://en.wikipedia.org/wiki/X.509
In which format is the certificate given at
http://www.telesec.de/downloads/DT-Root-CA-2.cer
then?
Now, the question is from where do I get a file which contains the
required certificates and the function
gnutls_certificate_set_x509_trust_file (cert_cred, CAFILE,
GNUTLS_X509_FMT_PEM)
is able to process?
Thanks for any help / suggestions.
Alex
--
http://home.chiemgau-net.de/ausserstorfer/