Fixed timeout.
[cert-checker.git] / main.c
1 /******************************************************************************
2  * Filename: main.c
3  * Description:
4  *
5  * Version: 1.0
6  * Created: Oct 09 2010 19:14:12
7  * Last modified: Oct 09 2010 19:14:12
8  *
9  * Author: Ladislav L├íska
10  * e-mail: ladislav.laska@gmail.com
11  *
12  ******************************************************************************/
13
14 #define _XOPEN_SOURCE 500
15
16 #include <gnutls/gnutls.h>
17 #include <gnutls/x509.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <netdb.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <time.h>
26 #include <signal.h>
27
28 #define S_UNREACHABLE -1
29 #define S_NO_X509 -2
30 #define S_OK 0
31 #define S_WARNING 1
32 #define S_ERROR 2
33 #define S_UNKNOWN 3
34
35 int warning_after = 30;
36 int error_after = 7;
37 int verbose = 0;
38 int timeout = 9;
39
40 #define LOG_LEVEL 0
41
42 #define die(msg) { fprintf(stderr, "Error: " msg "\n" ); exit(3); }
43 #define gnutls_die(code) { gnutls_perror(code); exit(3); }
44
45 char errmsg[256];
46
47 void print_help();
48
49 int tcp_open( char *hostname, char *service ) {
50         struct addrinfo hints;
51         struct addrinfo *result, *result_ptr;
52         int err, sfd;
53
54         /* Set hints */
55         memset(&hints, 0, sizeof(struct addrinfo));
56         hints.ai_family = AF_UNSPEC;
57         hints.ai_socktype = SOCK_STREAM;
58         err = getaddrinfo(hostname, service, &hints, &result);
59         if (err) {
60                 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
61                 exit(3);
62         }
63
64         for (result_ptr = result; result_ptr != NULL; result_ptr = result_ptr->ai_next) {
65                 sfd = socket(result_ptr->ai_family, result_ptr->ai_socktype,
66                                                 result_ptr->ai_protocol);
67                 if (sfd == -1) continue; 
68
69                 if (connect(sfd, result_ptr->ai_addr, result_ptr->ai_addrlen) != -1)
70                         break; /* Success */
71                 
72                 close(sfd);     /* connect(2) failed. */
73         }
74
75         if (result_ptr == NULL) {
76                 /* No address succeeded */
77                 return -1;
78         }
79
80         freeaddrinfo(result);
81
82         return sfd;
83 }
84
85 int check( char * hostname, char *service ) {
86         int state = S_OK;
87         int err;
88
89         gnutls_session_t session;
90         gnutls_certificate_credentials_t xcred;
91
92         /* x509 stuff */
93         
94         err = gnutls_certificate_allocate_credentials( &xcred );
95         if (err < 0) gnutls_die(err);
96
97         err = gnutls_init( &session, GNUTLS_CLIENT );
98         if (err < 0) gnutls_die(err);
99
100         /* priority init? */
101         err = gnutls_priority_set_direct(session, "EXPORT", NULL);
102         if (err < 0) gnutls_die(err);
103
104         err = gnutls_credentials_set( session, GNUTLS_CRD_CERTIFICATE, xcred );
105         if (err < 0) gnutls_die(err);
106
107         /* Connect to server */
108
109         long fd = tcp_open( hostname, service );
110         
111         if (fd == -1) {
112                 state= S_UNREACHABLE;
113                 goto cleanup;
114         }
115
116         /* Socket opened, establish tls connection */
117
118         /* Associate socket with session */
119         gnutls_transport_set_ptr( session, (gnutls_transport_ptr_t) fd );
120         
121         /* Do handshake */
122         err = gnutls_handshake( session );
123         if (err < 0) gnutls_die(err);
124         
125         /* Get server certificate. */
126         const gnutls_datum_t *cert_list;
127         unsigned int cert_list_size = 0;
128         time_t expiration_time, today;
129         gnutls_x509_crt_t cert;
130
131         if ( gnutls_certificate_type_get( session ) != GNUTLS_CRT_X509 ) {
132                 state = S_NO_X509;
133                 goto cleanup;
134         }
135
136         cert_list = gnutls_certificate_get_peers( session, &cert_list_size );
137
138         today = time(NULL);
139
140         for (int i = 0; i < cert_list_size; i++) {
141                 gnutls_x509_crt_init( &cert );
142                 gnutls_x509_crt_import( cert, &cert_list[0], GNUTLS_X509_FMT_DER );
143                 expiration_time = gnutls_x509_crt_get_expiration_time( cert );
144                 int expires_in = (expiration_time - today) / 86400;
145                 struct tm * t = gmtime( &expiration_time );
146                 if ((state == S_OK) && (expires_in <= warning_after)) {
147                         state = S_WARNING;
148                         sprintf(errmsg, "Warning - Will expire in %i days (%i-%02i-%02i).", expires_in, 
149                                 t->tm_year+1900, t->tm_mon+1, t->tm_mday );
150                 }
151                 if ((state <= S_WARNING) && (expires_in <= error_after)) {
152                         state = S_ERROR;
153                         sprintf(errmsg, "Critical - Will expire in %i days (%i-%02i-%02i).", expires_in,
154                                 t->tm_year+1900, t->tm_mon+1, t->tm_mday );
155                 }
156                 if (state == S_OK) {
157                         sprintf(errmsg, "OK - Will expire in %i days (%i-%02i-%02i).", expires_in,
158                                 t->tm_year+1900, t->tm_mon+1, t->tm_mday );
159                 }
160         }
161
162         /* Clean up */
163         err = gnutls_bye( session, GNUTLS_SHUT_WR );
164         if (err < 0) gnutls_die(err);
165         close( fd );
166         cleanup:
167         gnutls_deinit( session );
168         gnutls_certificate_free_credentials( xcred );
169
170         return state;
171 }
172
173 void log_func( int level, char *msg ) {
174         fprintf(stderr, "[%2i] %s", level, msg);
175 }
176
177 /* 
178  * This signal handler is wrong, but it's just a failsafe. 
179  */
180 void sig_handler(int k) {
181         fputs("Timeout.\n", stderr);    
182         exit(S_UNKNOWN);
183 }
184
185 int main(int argc, char **argv) {
186         char *hostname;
187         char *service = NULL;
188
189         int opt;
190
191         while ((opt = getopt(argc, argv, "hvw:c:H:p:s:t:")) != -1) {
192                 switch (opt) {
193                         case 'w':
194                                 warning_after = atoi(optarg);
195                                 break;
196                         case 'c':
197                                 error_after = atoi(optarg);
198                                 break;
199                         case 'H':
200                                 hostname = strdup(optarg);
201                                 break;
202                         case 't':
203                                 timeout = atoi( optarg );
204                                 if (timeout < 0) die("Timeout must be >= 0"); 
205                                 break;
206                         case 'p':                       
207                         case 's':
208                                 if (service != NULL) die("Only one service can be specified.");
209                                 service = strdup(optarg);
210                                 break;
211                         case 'h':
212                                 print_help();
213                                 exit(0);
214                         case 'v':
215                                 verbose++;
216                         default: break;
217                 }
218         }
219
220         if (argc <= 1) die("No address to try.");
221
222         gnutls_global_set_log_function((gnutls_log_func) log_func);
223         gnutls_global_set_log_level(LOG_LEVEL);
224
225
226         /* Initialize gnutls */
227         int err;
228         if ((err = gnutls_global_init())) {
229                 gnutls_perror(err);
230                 exit(3);
231         };
232
233         sprintf(errmsg, "OK");
234         int state = 0;
235
236         fflush(stdout);
237
238         /* Setup alarm */
239         /* TODO: doesn't work. */
240         signal(SIGALRM, sig_handler);
241         alarm( timeout );
242
243         /* Do checking */
244         state = check(hostname, service);
245         if (state < 0) {
246                 sprintf(errmsg, "Internal error %i.", state);
247                 state = S_UNKNOWN;
248         }
249         
250         gnutls_global_deinit();
251
252         free(hostname);
253         free(service);
254
255         printf("%s\n", errmsg);
256         return (state < 0) ? 127 : state;
257 }
258
259 void print_help() {
260         printf(
261                 "Usage: cert-checker [options] -H hostname -p|s port|service\n"
262                 "  Where options could be: \n"
263                 "       -h hostname   this help\n"
264                 "       -w n          warning level (in days, default 30)\n"
265                 "       -c n          critical level (in days, default 7)\n"
266                 "       -v            verbosity level\n"        
267                 "       -t n          timeout (in seconds, n=0 disables timeout\n"
268         );
269 }