rpms/crypto-utils/devel certwatch.c,1.14,1.15

Elio Maldonado emaldonado at fedoraproject.org
Wed Dec 24 22:19:55 UTC 2008


Author: emaldonado

Update of /cvs/extras/rpms/crypto-utils/devel
In directory cvs1.fedora.phx.redhat.com:/tmp/cvs-serv14436

Modified Files:
	certwatch.c 
Log Message:
Fix bugzilla 473860: certwatch does not warn of expiring certificates


Index: certwatch.c
===================================================================
RCS file: /cvs/extras/rpms/crypto-utils/devel/certwatch.c,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -r1.14 -r1.15
--- certwatch.c	19 Oct 2008 05:04:22 -0000	1.14
+++ certwatch.c	24 Dec 2008 22:19:24 -0000	1.15
@@ -5,16 +5,16 @@
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
-  
+
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
-  
+
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-   
+
    In addition, as a special exception, Red Hat, Inc. gives permission
    to link the code of this program with the OpenSSL library (or with
    modified versions of OpenSSL that use the same license as OpenSSL),
@@ -84,6 +84,7 @@
 #include <secmod.h>
 #include <base64.h>
 #include <seccomon.h>
+#include <certt.h>
 
 #include <stdio.h>
 #include <string.h>
@@ -93,11 +94,11 @@
 
 #define TIME_BUF_SIZE 100
 
-/* Return a certificate structure from a pem-encoded cert in a file; 
+/* Return a certificate structure from a pem-encoded cert in a file;
  * or NULL on failure. Semantics similar to the OpenSSL call
  * PEM_read_X509(fp, NULL, NULL, NULL);
  */
-extern CERTCertificate * 
+extern CERTCertificate *
 PEMUTIL_PEM_read_X509(const char *filename);
 
 /* size big enough for formatting time buffer */
@@ -106,27 +107,11 @@
 static int warn_period = 30;
 static char *warn_address = "root";
 
-/* Format a PRTime value into a buffer with format "%a %b %d %H:%M:%S %Y"
- * and return the buffer. The buffer returned must the freed with PORT_Free.
- * Semantics are similar to asctime. 
- */
-char * AsciiTime(PRTime time)
-{   
-    PRExplodedTime printable;
-    char *timebuf;
-    
-    PR_ExplodeTime(time, PR_GMTParameters, &printable);
-    timebuf = PORT_Alloc(TIME_BUF_SIZE);
-    (void) PR_FormatTime(timebuf, TIME_BUF_SIZE, "%a %b %d %H:%M:%S %Y", &printable);
-    if (strlen(timebuf) < TIME_BUF_SIZE) timebuf[strlen(timebuf)] = '\0';
-    return (timebuf);
-}
-
-/* Uses the password passed in the -f(pwfile) argument of the command line.  
+/* Uses the password passed in the -f(pwfile) argument of the command line.
  * After use once, null it out otherwise PKCS11 calls us forever.?
- * 
+ *
  * Code based on SECU_GetModulePassword from the Mozilla NSS secutils
- * imternal librart. 
+ * internal library.
  */
 static char *GetModulePassword(PK11SlotInfo *slot, PRBool retry, void *arg)
 {
@@ -137,12 +122,12 @@
     char *pwFile = arg;
 
     if (!pwFile) return 0;
-    if (retry) return 0; /* no good retrying - file contents will be the same */ 
+    if (retry) return 0; /* no good retrying - file contents will be the same */
     if (!(fd = PR_Open(pwFile, PR_RDONLY, 0))) return 0;
 
     nb = PR_Read(fd, phrase, sizeof(phrase));
     PR_Close(fd);
-    
+
     /* handle the Windows EOL case */
     i = 0;
     while (phrase[i] != '\r' && phrase[i] != '\n' && i < nb) i++;
@@ -164,30 +149,81 @@
     return buf;
 }
 
+
+/* A year is leap iff is divisible by 4 but not by 100 or is divisible by 400 */
+static int leap_year(int year) {
+    return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ? 1 : 0;
+}
+
+/* Computes the day difference among two PRTime's */
+static int diff_time_days(PRTime aT, PRTime bT)
+{
+    PRTime am1, one;
+    PRExplodedTime a, b;
+    int years, days = 0;
+
+    LL_I2L(one, 1);
+
+    do {
+	PR_ExplodeTime(aT, PR_GMTParameters, &a);
+	PR_ExplodeTime(bT, PR_GMTParameters, &b);
+
+	years = a.tm_year - b.tm_year;
+	if (years < 0) break;
+
+	if (years == 0) {
+            days += (a.tm_yday - b.tm_yday);
+	} else if (a.tm_year == b.tm_year + 1) {
+            days += (365 + leap_year(b.tm_year) - b.tm_yday + a.tm_yday);
+	} else {
+            LL_SUB(am1, aT, one);
+            aT = am1;
+	}
+
+    } while (0);
+
+    return days;
+}
+
 /* Print a warning message that the certificate in 'filename', issued
  * to hostname 'hostname', will expire (or has expired). */
 static int warning(FILE *out, const char *filename, const char *hostname,
-                   SECCertTimeValidity validity, 
+                   SECCertTimeValidity validity,
                    PRTime start, PRTime end, PRTime now, int quiet)
 {
     /* Note that filename can be the cert nickname. */
     int renew = 1;
     char subj[50];
-    PRTime prtimeDiff;
-    
-    LL_SUB(prtimeDiff, end, start);
-    
-    if ( LL_CMP(start, >, now) ) {   
+
+    switch (validity) {
+    case secCertTimeNotValidYet:
         strcpy(subj, "is not yet valid");
         renew = 0;
-    } else if (LL_EQ(now, end)) {
-        strcpy(subj, "will expire today");
-    } else if (LL_EQ(prtimeDiff, 1)) {
-        sprintf(subj, "will expire tomorrow");
-    } else if (LL_CMP(prtimeDiff, <, warn_period)) {
-        sprintf(subj, "will expire on %s", AsciiTime(end));
-    } else {
-        return 0; /* nothing to warn about. */
+        break;
+    case secCertTimeExpired:
+        sprintf(subj, "has expired");
+        break;
+    case secCertTimeValid:
+        {
+            /* days till expiry */
+            int days = diff_time_days(end, now);
+            if (days == 0) {
+                strcpy(subj, "will expire today");
+            } else if (days == 1) {
+                sprintf(subj, "will expire tomorrow");
+            } else if (days < warn_period) {
+                sprintf(subj, "will expire in %d days", days);
+            } else {
+                return 0; /* nothing to warn about. */
+            }
+	}
+	break;
+    case secCertTimeUndetermined:
+    default:
+        /* it will never get here if caller checks validity */
+        strcpy(subj, "validity could not be decoded from the cert");
+        renew = 0;
+        break;
     }
 
     if (quiet) return 1;
@@ -195,11 +231,11 @@
     fprintf(out, "To: %s\n", warn_address);
     fprintf(out, "Subject: The certificate for %s %s\n", hostname, subj);
     fputs("\n", out);
-    
-    fprintf(out, 
+
+    fprintf(out,
             " ################# SSL Certificate Warning ################\n\n");
 
-    fprintf(out, 
+    fprintf(out,
             "  Certificate for hostname '%s', in file (or by nickname):\n"
             "     %s\n\n",
             hostname, filename);
@@ -215,10 +251,10 @@
         char *result = pr_ctime(start, until, TIME_SIZE);
         assert(result == until);
         if (strlen(until) < sizeof(until)) until[strlen(until)] = '\0';
-        fprintf(out, 
+        fprintf(out,
                 "  The certificate is not valid until %s.\n\n"
                 "  Browsers will not be able to correctly connect to this\n"
-                "  web site using SSL until the certificate becomes valid.\n", 
+                "  web site using SSL until the certificate becomes valid.\n",
                 until);
     }
 
@@ -252,30 +288,30 @@
  * issue a warning message if 'quiet' is zero.  If quiet is non-zero,
  * returns one to indicate that a warning would have been issued, zero
  * to indicate no warning would be issued, or -1 if an error
- * occurred. 
+ * occurred.
  *
  * When byNickname is 1 then 'name' is a nickname to search
  * for in the database otherwise it's the certificate file.
  */
-static int check_cert(const char *name, int byNickname, int quiet) 
+static int check_cert(const char *name, int byNickname, int quiet)
 {
     CERTCertificate *cert;
     SECCertTimeValidity validity;
     PRTime notBefore, notAfter;
     char cname[128];
-    
+
     int doWarning = 0;
 
     /* parse the cert */
-    cert = byNickname 
+    cert = byNickname
         ? CERT_FindCertByNickname(CERT_GetDefaultCertDB(), (char *)name)
         : PEMUTIL_PEM_read_X509(name);
     if (cert == NULL) return -1;
-    
+
     /* determine the validity period of the cert. */
     validity = CERT_CheckCertValidTimes(cert, PR_Now(), PR_FALSE);
     if (validity == secCertTimeUndetermined) goto cleanup;
-    
+
     /* get times out of the cert */
     if (CERT_GetCertTimes(cert, &notBefore, &notAfter)
         != SECSuccess) goto cleanup;
@@ -295,7 +331,7 @@
     if (cert) CERT_DestroyCertificate(cert);
     if (!doWarning) return -1;
 
-    return warning(stdout, name, cname, validity, 
+    return warning(stdout, name, cname, validity,
                    notBefore, notAfter, PR_Now(), quiet);
 }
 
@@ -312,8 +348,8 @@
         { "keydbprexix", required_argument, NULL, 'k' },
         { NULL }
     };
-
-    char *certDBPrefix = ""; 
+    const char *opts = "qp:a:d:w:c:k:";
+    char *certDBPrefix = "";
     char *keyDBPrefix = "";
 
     char *configdir = NULL;    /* contains the cert database */
@@ -323,8 +359,8 @@
     /* The 'timezone' global is needed to adjust local times from
      * mktime() back to UTC: */
     tzset();
-    
-    while ((optc = getopt_long(argc, argv, "qp:a:d:w:c:k:", options, NULL)) != -1) {
+
+    while ((optc = getopt_long(argc, argv, opts, options, NULL)) != -1) {
         switch (optc) {
         case 'q':
             quiet = 1;
@@ -353,18 +389,18 @@
             break;
         }
     }
-    
+
     /* NSS initialization */
 
     if (byNickname) {
         /* cert in database */
-        if (NSS_Initialize(configdir, certDBPrefix, keyDBPrefix, 
+        if (NSS_Initialize(configdir, certDBPrefix, keyDBPrefix,
                    SECMOD_DB, NSS_INIT_READONLY) != SECSuccess) {
             return EXIT_FAILURE;
         }
         /* in case module requires a password */
         if (passwordfile) {
-            PK11_SetPasswordFunc(GetModulePassword);  
+            PK11_SetPasswordFunc(GetModulePassword);
         }
     } else {
         /* cert in a pem file */
@@ -372,13 +408,14 @@
         if (!certDir) {
             certDir = "/etc/pki/nssdb";
         }
-        if (NSS_Initialize(certDir, certDBPrefix, keyDBPrefix, 
+        if (NSS_Initialize(certDir, certDBPrefix, keyDBPrefix,
                    SECMOD_DB, NSS_INIT_READONLY) != SECSuccess) {
             printf("NSS_Init(\"%s\") failed\n", certDir);
             return EXIT_FAILURE;
         }
     }
 
-    /* When byNickname is 1 argv[optind] is a nickname otherwise a filename. */ 
-    return check_cert(argv[optind], byNickname, quiet) == 1 ? EXIT_SUCCESS : EXIT_FAILURE;
+    /* When byNickname is 1 argv[optind] is a nickname otherwise a filename. */
+    return check_cert(argv[optind], byNickname, quiet) == 1
+                      ? EXIT_SUCCESS : EXIT_FAILURE;
 }




More information about the fedora-extras-commits mailing list