1 /******************************************************************************
6 * Created: Oct 09 2010 19:14:12
7 * Last modified: Oct 09 2010 19:14:12
9 * Author: Ladislav Láska
10 * e-mail: ladislav.laska@gmail.com
12 ******************************************************************************/
14 #define _XOPEN_SOURCE 500
16 #include <gnutls/gnutls.h>
17 #include <gnutls/x509.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
28 #define S_UNREACHABLE -1
35 int warning_after = 30;
41 #define BUFFER_SIZE 1024
45 #define die(msg) { fprintf(stderr, "Error: " msg "\n" ); exit(3); }
46 #define dief(msg, ...) { fprintf(stderr, "Error: " msg "\n", __VA_ARGS__ ); exit(3); }
47 #define gnutls_die(code) { gnutls_perror(code); exit(3); }
53 int tcp_open( char *hostname, char *service ) {
54 struct addrinfo hints;
55 struct addrinfo *result, *result_ptr;
59 memset(&hints, 0, sizeof(struct addrinfo));
60 hints.ai_family = AF_UNSPEC;
61 hints.ai_socktype = SOCK_STREAM;
62 err = getaddrinfo(hostname, service, &hints, &result);
64 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
68 for (result_ptr = result; result_ptr != NULL; result_ptr = result_ptr->ai_next) {
69 sfd = socket(result_ptr->ai_family, result_ptr->ai_socktype,
70 result_ptr->ai_protocol);
71 if (sfd == -1) continue;
73 if (connect(sfd, result_ptr->ai_addr, result_ptr->ai_addrlen) != -1)
76 close(sfd); /* connect(2) failed. */
79 if (result_ptr == NULL) {
80 /* No address succeeded */
89 /* Expects some data. This is just wrong and should be written correctly. */
90 char *expect(int fd, char *str) {
91 char buffer[BUFFER_SIZE];
92 int len = strlen(str);
93 int bytes = read(fd, buffer, BUFFER_SIZE);
94 if (bytes < len) goto fail;
95 if (!strncmp(buffer,str,len)) {
96 while (bytes == BUFFER_SIZE)
97 bytes = read(fd, buffer, BUFFER_SIZE) > 0;
102 char *ptr = strdup(buffer);
103 while (bytes == BUFFER_SIZE)
104 bytes = read(fd, buffer, BUFFER_SIZE) > 0;
108 int check( char * hostname, char *service ) {
112 gnutls_session_t session;
113 gnutls_certificate_credentials_t xcred;
117 err = gnutls_certificate_allocate_credentials( &xcred );
118 if (err < 0) gnutls_die(err);
120 err = gnutls_init( &session, GNUTLS_CLIENT );
121 if (err < 0) gnutls_die(err);
124 err = gnutls_priority_set_direct(session, "EXPORT", NULL);
125 if (err < 0) gnutls_die(err);
127 err = gnutls_credentials_set( session, GNUTLS_CRD_CERTIFICATE, xcred );
128 if (err < 0) gnutls_die(err);
130 /* Connect to server */
132 long fd = tcp_open( hostname, service );
135 state= S_UNREACHABLE;
144 char buffer[] = "STARTTLS\n";
145 write(fd, buffer, sizeof(buffer));
147 if ((b = expect(fd, "220 "))) {
148 dief("STARTTLS declined: %s.", b);
153 /* Socket opened, establish tls connection */
155 /* Associate socket with session */
156 gnutls_transport_set_ptr( session, (gnutls_transport_ptr_t) fd );
159 err = gnutls_handshake( session );
160 if (err < 0) gnutls_die(err);
162 /* Get server certificate. */
163 const gnutls_datum_t *cert_list;
164 unsigned int cert_list_size = 0;
165 time_t expiration_time, today;
166 gnutls_x509_crt_t cert;
168 if ( gnutls_certificate_type_get( session ) != GNUTLS_CRT_X509 ) {
173 cert_list = gnutls_certificate_get_peers( session, &cert_list_size );
177 for (int i = 0; i < cert_list_size; i++) {
178 gnutls_x509_crt_init( &cert );
179 gnutls_x509_crt_import( cert, &cert_list[0], GNUTLS_X509_FMT_DER );
180 expiration_time = gnutls_x509_crt_get_expiration_time( cert );
181 int expires_in = (expiration_time - today) / 86400;
182 struct tm * t = gmtime( &expiration_time );
183 if ((state == S_OK) && (expires_in <= warning_after)) {
185 sprintf(errmsg, "Warning - Will expire in %i days (%i-%02i-%02i).", expires_in,
186 t->tm_year+1900, t->tm_mon+1, t->tm_mday );
188 if ((state <= S_WARNING) && (expires_in <= error_after)) {
190 sprintf(errmsg, "Critical - Will expire in %i days (%i-%02i-%02i).", expires_in,
191 t->tm_year+1900, t->tm_mon+1, t->tm_mday );
194 sprintf(errmsg, "OK - Will expire in %i days (%i-%02i-%02i).", expires_in,
195 t->tm_year+1900, t->tm_mon+1, t->tm_mday );
200 err = gnutls_bye( session, GNUTLS_SHUT_WR );
201 if (err < 0) gnutls_die(err);
204 gnutls_deinit( session );
205 gnutls_certificate_free_credentials( xcred );
210 void log_func( int level, char *msg ) {
211 fprintf(stderr, "[%2i] %s", level, msg);
215 * This signal handler is wrong, but it's just a failsafe.
217 void sig_handler(int k) {
218 fputs("Timeout.\n", stderr);
222 int main(int argc, char **argv) {
224 char *service = NULL;
228 while ((opt = getopt(argc, argv, "hvSw:c:H:p:s:t:")) != -1) {
231 warning_after = atoi(optarg);
234 error_after = atoi(optarg);
237 hostname = strdup(optarg);
240 timeout = atoi( optarg );
241 if (timeout < 0) die("Timeout must be >= 0");
245 if (service != NULL) die("Only one service can be specified.");
246 service = strdup(optarg);
259 if (argc <= 1) die("No address to try.");
261 gnutls_global_set_log_function((gnutls_log_func) log_func);
262 gnutls_global_set_log_level(LOG_LEVEL);
265 /* Initialize gnutls */
267 if ((err = gnutls_global_init())) {
272 sprintf(errmsg, "OK");
278 /* TODO: doesn't work. */
279 signal(SIGALRM, sig_handler);
283 state = check(hostname, service);
285 sprintf(errmsg, "Internal error %i.", state);
289 gnutls_global_deinit();
294 printf("%s\n", errmsg);
295 return (state < 0) ? 127 : state;
300 "Usage: cert-checker [options] -H hostname -p|s port|service\n"
301 " Where options could be: \n"
302 " -h hostname this help\n"
303 " -w n warning level (in days, default 30)\n"
304 " -c n critical level (in days, default 7)\n"
305 " -v verbosity level\n"
306 " -t n timeout (in seconds, n=0 disables timeout\n"
307 " -S use SMTP/STARTLS\n"