Basic support for SMTP STARTTLS.
[cert-checker.git] / main.c
diff --git a/main.c b/main.c
index 68f50e57704947d593dc3c8fc4574475d09a9a2b..3a8dab5b40cdf15f51b44cf19421dca996794996 100644 (file)
--- a/main.c
+++ b/main.c
 
 int warning_after = 30;
 int error_after = 7;
+int verbose = 0;
+int timeout = 9;
+int use_starttls = 0;
+
+#define BUFFER_SIZE 1024
 
 #define LOG_LEVEL 0
 
 #define die(msg) { fprintf(stderr, "Error: " msg "\n" ); exit(3); }
+#define dief(msg, ...) { fprintf(stderr, "Error: " msg "\n", __VA_ARGS__ ); exit(3); }
 #define gnutls_die(code) { gnutls_perror(code); exit(3); }
 
 char errmsg[256];
@@ -80,6 +86,25 @@ int tcp_open( char *hostname, char *service ) {
        return sfd;
 }
 
+/* Expects some data. This is just wrong and should be written correctly. */
+char *expect(int fd, char *str) {
+       char buffer[BUFFER_SIZE];
+       int len = strlen(str);
+       int bytes = read(fd, buffer, BUFFER_SIZE);
+       if (bytes < len) goto fail;
+       if (!strncmp(buffer,str,len)) {
+               while (bytes == BUFFER_SIZE)
+                       bytes = read(fd, buffer, BUFFER_SIZE) > 0;
+               return NULL;
+       }
+
+       fail:;
+       char *ptr = strdup(buffer);
+       while (bytes == BUFFER_SIZE)
+               bytes = read(fd, buffer, BUFFER_SIZE) > 0;
+       return ptr;
+}
+
 int check( char * hostname, char *service ) {
        int state = S_OK;
        int err;
@@ -104,13 +129,27 @@ int check( char * hostname, char *service ) {
 
        /* Connect to server */
 
-       int fd = tcp_open( hostname, service );
+       long fd = tcp_open( hostname, service );
        
        if (fd == -1) {
                state= S_UNREACHABLE;
                goto cleanup;
        }
 
+
+       /* StartTLS? */
+
+       if (use_starttls) {
+               expect(fd, "");
+               char buffer[] = "STARTTLS\n";
+               write(fd, buffer, sizeof(buffer));
+               char *b;
+               if ((b = expect(fd, "220 "))) {
+                       dief("STARTTLS declined: %s.", b);
+               }
+       }
+
+
        /* Socket opened, establish tls connection */
 
        /* Associate socket with session */
@@ -140,18 +179,23 @@ int check( char * hostname, char *service ) {
                gnutls_x509_crt_import( cert, &cert_list[0], GNUTLS_X509_FMT_DER );
                expiration_time = gnutls_x509_crt_get_expiration_time( cert );
                int expires_in = (expiration_time - today) / 86400;
+               struct tm * t = gmtime( &expiration_time );
                if ((state == S_OK) && (expires_in <= warning_after)) {
                        state = S_WARNING;
-                       sprintf(errmsg, "Certificate will expire in %i days.", expires_in);
+                       sprintf(errmsg, "Warning - Will expire in %i days (%i-%02i-%02i).", expires_in, 
+                               t->tm_year+1900, t->tm_mon+1, t->tm_mday );
                }
                if ((state <= S_WARNING) && (expires_in <= error_after)) {
                        state = S_ERROR;
-                       sprintf(errmsg, "Certificate will expire in %i days.", expires_in);
+                       sprintf(errmsg, "Critical - Will expire in %i days (%i-%02i-%02i).", expires_in,
+                               t->tm_year+1900, t->tm_mon+1, t->tm_mday );
+               }
+               if (state == S_OK) {
+                       sprintf(errmsg, "OK - Will expire in %i days (%i-%02i-%02i).", expires_in,
+                               t->tm_year+1900, t->tm_mon+1, t->tm_mday );
                }
        }
 
-       printf("Got %i certs.", cert_list_size);
-
        /* Clean up */
        err = gnutls_bye( session, GNUTLS_SHUT_WR );
        if (err < 0) gnutls_die(err);
@@ -171,7 +215,7 @@ void log_func( int level, char *msg ) {
  * This signal handler is wrong, but it's just a failsafe. 
  */
 void sig_handler(int k) {
-       fputs("Timeout.", stderr);      
+       fputs("Timeout.\n", stderr);    
        exit(S_UNKNOWN);
 }
 
@@ -181,7 +225,7 @@ int main(int argc, char **argv) {
 
        int opt;
 
-       while ((opt = getopt(argc, argv, "hvw:c:H:p:s:")) != -1) {
+       while ((opt = getopt(argc, argv, "hvSw:c:H:p:s:t:")) != -1) {
                switch (opt) {
                        case 'w':
                                warning_after = atoi(optarg);
@@ -192,6 +236,10 @@ int main(int argc, char **argv) {
                        case 'H':
                                hostname = strdup(optarg);
                                break;
+                       case 't':
+                               timeout = atoi( optarg );
+                               if (timeout < 0) die("Timeout must be >= 0"); 
+                               break;
                        case 'p':                       
                        case 's':
                                if (service != NULL) die("Only one service can be specified.");
@@ -200,6 +248,10 @@ int main(int argc, char **argv) {
                        case 'h':
                                print_help();
                                exit(0);
+                       case 'v':
+                               verbose++;
+                       case 'S':
+                               use_starttls = 1;
                        default: break;
                }
        }
@@ -225,12 +277,14 @@ int main(int argc, char **argv) {
        /* Setup alarm */
        /* TODO: doesn't work. */
        signal(SIGALRM, sig_handler);
-       alarm(9*60);
+       alarm( timeout );
 
        /* Do checking */
        state = check(hostname, service);
-       if (state < 0)
-               printf("Internal error.");
+       if (state < 0) {
+               sprintf(errmsg, "Internal error %i.", state);
+               state = S_UNKNOWN;
+       }
        
        gnutls_global_deinit();
 
@@ -242,5 +296,14 @@ int main(int argc, char **argv) {
 }
 
 void print_help() {
-       printf("Help yourself.");
+       printf(
+               "Usage: cert-checker [options] -H hostname -p|s port|service\n"
+               "  Where options could be: \n"
+               "       -h hostname   this help\n"
+               "       -w n          warning level (in days, default 30)\n"
+               "       -c n          critical level (in days, default 7)\n"
+               "       -v            verbosity level\n"        
+               "       -t n          timeout (in seconds, n=0 disables timeout\n"
+               "               -S            use SMTP/STARTLS\n"
+       );
 }