Updated sample config, removed debugging message.
[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
38 #define LOG_LEVEL 0
39
40 #define die(msg) { fprintf(stderr, "Error: " msg "\n" ); exit(3); }
41 #define gnutls_die(code) { gnutls_perror(code); exit(3); }
42
43 char errmsg[256];
44
45 void print_help();
46
47 int tcp_open( char *hostname, char *service ) {
48         struct addrinfo hints;
49         struct addrinfo *result, *result_ptr;
50         int err, sfd;
51
52         /* Set hints */
53         memset(&hints, 0, sizeof(struct addrinfo));
54         hints.ai_family = AF_UNSPEC;
55         hints.ai_socktype = SOCK_STREAM;
56         err = getaddrinfo(hostname, service, &hints, &result);
57         if (err) {
58                 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
59                 exit(3);
60         }
61
62         for (result_ptr = result; result_ptr != NULL; result_ptr = result_ptr->ai_next) {
63                 sfd = socket(result_ptr->ai_family, result_ptr->ai_socktype,
64                                                 result_ptr->ai_protocol);
65                 if (sfd == -1) continue; 
66
67                 if (connect(sfd, result_ptr->ai_addr, result_ptr->ai_addrlen) != -1)
68                         break; /* Success */
69                 
70                 close(sfd);     /* connect(2) failed. */
71         }
72
73         if (result_ptr == NULL) {
74                 /* No address succeeded */
75                 return -1;
76         }
77
78         freeaddrinfo(result);
79
80         return sfd;
81 }
82
83 int check( char * hostname, char *service ) {
84         int state = S_OK;
85         int err;
86
87         gnutls_session_t session;
88         gnutls_certificate_credentials_t xcred;
89
90         /* x509 stuff */
91         
92         err = gnutls_certificate_allocate_credentials( &xcred );
93         if (err < 0) gnutls_die(err);
94
95         err = gnutls_init( &session, GNUTLS_CLIENT );
96         if (err < 0) gnutls_die(err);
97
98         /* priority init? */
99         err = gnutls_priority_set_direct(session, "EXPORT", NULL);
100         if (err < 0) gnutls_die(err);
101
102         err = gnutls_credentials_set( session, GNUTLS_CRD_CERTIFICATE, xcred );
103         if (err < 0) gnutls_die(err);
104
105         /* Connect to server */
106
107         int fd = tcp_open( hostname, service );
108         
109         if (fd == -1) {
110                 state= S_UNREACHABLE;
111                 goto cleanup;
112         }
113
114         /* Socket opened, establish tls connection */
115
116         /* Associate socket with session */
117         gnutls_transport_set_ptr( session, (gnutls_transport_ptr_t) fd );
118         
119         /* Do handshake */
120         err = gnutls_handshake( session );
121         if (err < 0) gnutls_die(err);
122         
123         /* Get server certificate. */
124         const gnutls_datum_t *cert_list;
125         unsigned int cert_list_size = 0;
126         time_t expiration_time, today;
127         gnutls_x509_crt_t cert;
128
129         if ( gnutls_certificate_type_get( session ) != GNUTLS_CRT_X509 ) {
130                 state = S_NO_X509;
131                 goto cleanup;
132         }
133
134         cert_list = gnutls_certificate_get_peers( session, &cert_list_size );
135
136         today = time(NULL);
137
138         for (int i = 0; i < cert_list_size; i++) {
139                 gnutls_x509_crt_init( &cert );
140                 gnutls_x509_crt_import( cert, &cert_list[0], GNUTLS_X509_FMT_DER );
141                 expiration_time = gnutls_x509_crt_get_expiration_time( cert );
142                 int expires_in = (expiration_time - today) / 86400;
143                 if ((state == S_OK) && (expires_in <= warning_after)) {
144                         state = S_WARNING;
145                         sprintf(errmsg, "Certificate will expire in %i days.", expires_in);
146                 }
147                 if ((state <= S_WARNING) && (expires_in <= error_after)) {
148                         state = S_ERROR;
149                         sprintf(errmsg, "Certificate will expire in %i days.", expires_in);
150                 }
151         }
152
153         /* Clean up */
154         err = gnutls_bye( session, GNUTLS_SHUT_WR );
155         if (err < 0) gnutls_die(err);
156         close( fd );
157         cleanup:
158         gnutls_deinit( session );
159         gnutls_certificate_free_credentials( xcred );
160
161         return state;
162 }
163
164 void log_func( int level, char *msg ) {
165         fprintf(stderr, "[%2i] %s", level, msg);
166 }
167
168 /* 
169  * This signal handler is wrong, but it's just a failsafe. 
170  */
171 void sig_handler(int k) {
172         fputs("Timeout.", stderr);      
173         exit(S_UNKNOWN);
174 }
175
176 int main(int argc, char **argv) {
177         char *hostname;
178         char *service = NULL;
179
180         int opt;
181
182         while ((opt = getopt(argc, argv, "hvw:c:H:p:s:")) != -1) {
183                 switch (opt) {
184                         case 'w':
185                                 warning_after = atoi(optarg);
186                                 break;
187                         case 'c':
188                                 error_after = atoi(optarg);
189                                 break;
190                         case 'H':
191                                 hostname = strdup(optarg);
192                                 break;
193                         case 'p':                       
194                         case 's':
195                                 if (service != NULL) die("Only one service can be specified.");
196                                 service = strdup(optarg);
197                                 break;
198                         case 'h':
199                                 print_help();
200                                 exit(0);
201                         default: break;
202                 }
203         }
204
205         if (argc <= 1) die("No address to try.");
206
207         gnutls_global_set_log_function((gnutls_log_func) log_func);
208         gnutls_global_set_log_level(LOG_LEVEL);
209
210
211         /* Initialize gnutls */
212         int err;
213         if ((err = gnutls_global_init())) {
214                 gnutls_perror(err);
215                 exit(3);
216         };
217
218         sprintf(errmsg, "OK");
219         int state = 0;
220
221         fflush(stdout);
222
223         /* Setup alarm */
224         /* TODO: doesn't work. */
225         signal(SIGALRM, sig_handler);
226         alarm(9*60);
227
228         /* Do checking */
229         state = check(hostname, service);
230         if (state < 0)
231                 printf("Internal error.");
232         
233         gnutls_global_deinit();
234
235         free(hostname);
236         free(service);
237
238         printf("%s\n", errmsg);
239         return (state < 0) ? 127 : state;
240 }
241
242 void print_help() {
243         printf("Help yourself.");
244 }