--- /dev/null
+#include "timeStr.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <security_utilities/threading.h> /* for Mutex */
+#include <utilLib/common.h>
+
+/*
+ * Given a string containing either a UTC-style or "generalized time"
+ * time string, convert to a struct tm (in GMT/UTC). Returns nonzero on
+ * error.
+ */
+int appTimeStringToTm(
+ const char *str,
+ unsigned len,
+ struct tm *tmp)
+{
+ char szTemp[5];
+ unsigned isUtc;
+ unsigned x;
+ unsigned i;
+ char *cp;
+
+ if((str == NULL) || (len == 0) || (tmp == NULL)) {
+ return 1;
+ }
+
+ /* tolerate NULL terminated or not */
+ if(str[len - 1] == '\0') {
+ len--;
+ }
+ switch(len) {
+ case UTC_TIME_STRLEN: // 2-digit year, not Y2K compliant
+ isUtc = 1;
+ break;
+ case GENERALIZED_TIME_STRLEN: // 4-digit year
+ isUtc = 0;
+ break;
+ default: // unknown format
+ return 1;
+ }
+
+ cp = (char *)str;
+
+ /* check that all characters except last are digits */
+ for(i=0; i<(len - 1); i++) {
+ if ( !(isdigit(cp[i])) ) {
+ return 1;
+ }
+ }
+
+ /* check last character is a 'Z' */
+ if(cp[len - 1] != 'Z' ) {
+ return 1;
+ }
+
+ /* YEAR */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ if(!isUtc) {
+ /* two more digits */
+ szTemp[2] = *cp++;
+ szTemp[3] = *cp++;
+ szTemp[4] = '\0';
+ }
+ else {
+ szTemp[2] = '\0';
+ }
+ x = atoi( szTemp );
+ if(isUtc) {
+ /*
+ * 2-digit year.
+ * 0 <= year < 50 : assume century 21
+ * 50 <= year < 70 : illegal per PKIX
+ * 70 < year <= 99 : assume century 20
+ */
+ if(x < 50) {
+ x += 2000;
+ }
+ else if(x < 70) {
+ return 1;
+ }
+ else {
+ /* century 20 */
+ x += 1900;
+ }
+ }
+ /* by definition - tm_year is year - 1900 */
+ tmp->tm_year = x - 1900;
+
+ /* MONTH */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ szTemp[2] = '\0';
+ x = atoi( szTemp );
+ /* in the string, months are from 1 to 12 */
+ if((x > 12) || (x <= 0)) {
+ return 1;
+ }
+ /* in a tm, 0 to 11 */
+ tmp->tm_mon = x - 1;
+
+ /* DAY */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ szTemp[2] = '\0';
+ x = atoi( szTemp );
+ /* 1..31 in both formats */
+ if((x > 31) || (x <= 0)) {
+ return 1;
+ }
+ tmp->tm_mday = x;
+
+ /* HOUR */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ szTemp[2] = '\0';
+ x = atoi( szTemp );
+ if((x > 23) || (x < 0)) {
+ return 1;
+ }
+ tmp->tm_hour = x;
+
+ /* MINUTE */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ szTemp[2] = '\0';
+ x = atoi( szTemp );
+ if((x > 59) || (x < 0)) {
+ return 1;
+ }
+ tmp->tm_min = x;
+
+ /* SECOND */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ szTemp[2] = '\0';
+ x = atoi( szTemp );
+ if((x > 59) || (x < 0)) {
+ return 1;
+ }
+ tmp->tm_sec = x;
+ return 0;
+}
+
+/* common time routine used by utcAtNowPlus and genTimeAtNowPlus */
+#define MAX_TIME_STR_LEN 30
+
+static Mutex timeMutex; // protects time(), gmtime()
+
+char *appTimeAtNowPlus(int secFromNow,
+ timeSpec spec)
+{
+ struct tm utc;
+ char *outStr;
+ time_t baseTime;
+
+ timeMutex.lock();
+ baseTime = time(NULL);
+ baseTime += (time_t)secFromNow;
+ utc = *gmtime(&baseTime);
+ timeMutex.unlock();
+
+ outStr = (char *)CSSM_MALLOC(MAX_TIME_STR_LEN);
+
+ switch(spec) {
+ case TIME_UTC:
+ /* UTC - 2 year digits - code which parses this assumes that
+ * (2-digit) years between 0 and 49 are in century 21 */
+ if(utc.tm_year >= 100) {
+ utc.tm_year -= 100;
+ }
+ sprintf(outStr, "%02d%02d%02d%02d%02d%02dZ",
+ utc.tm_year /* + 1900 */, utc.tm_mon + 1,
+ utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec);
+ break;
+ case TIME_GEN:
+ sprintf(outStr, "%04d%02d%02d%02d%02d%02dZ",
+ /* note year is relative to 1900, hopefully it'll have four valid
+ * digits! */
+ utc.tm_year + 1900, utc.tm_mon + 1,
+ utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec);
+ break;
+ case TIME_CSSM:
+ sprintf(outStr, "%04d%02d%02d%02d%02d%02d",
+ /* note year is relative to 1900, hopefully it'll have
+ * four valid digits! */
+ utc.tm_year + 1900, utc.tm_mon + 1,
+ utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec);
+ break;
+ }
+ return outStr;
+}
+
+/*
+ * Malloc and return UTC (2-digit year) time string, for time with specified
+ * offset from present. This uses the stdlib gmtime(), which is not thread safe.
+ * Even though this function protects the call with a lock, the TP also uses
+ * gmtime. It also does the correct locking for its own calls to gmtime()Êbut
+ * the is no way to synchronize TP's calls to gmtime() with the calls to this
+ * one other than only using this one when no threads might be performing TP ops.
+ */
+char *utcAtNowPlus(int secFromNow)
+{
+ return appTimeAtNowPlus(secFromNow, TIME_UTC);
+}
+
+/*
+ * Same thing, generalized time (4-digit year).
+ */
+char *genTimeAtNowPlus(int secFromNow)
+{
+ return appTimeAtNowPlus(secFromNow, TIME_GEN);
+}
+
+/*
+ * Free the string obtained from the above.
+ */
+void freeTimeString(char *timeStr)
+{
+ CSSM_FREE(timeStr);
+}
+
+/*
+ * Convert a CSSM_X509_TIME, which can be in any of three forms (UTC,
+ * generalized, or CSSM_TIMESTRING) into a CSSM_TIMESTRING. Caller
+ * must free() the result. Returns NULL if x509time is badly formed.
+ */
+char *x509TimeToCssmTimestring(
+ const CSSM_X509_TIME *x509Time,
+ unsigned *rtnLen) // for caller's convenience
+{
+ int len = x509Time->time.Length;
+ const char *inStr = (char *)x509Time->time.Data; // not NULL terminated!
+ char *rtn;
+
+ *rtnLen = 0;
+ if((len == 0) || (inStr == NULL)) {
+ return NULL;
+ }
+ rtn = (char *)malloc(CSSM_TIME_STRLEN + 1);
+ rtn[0] = '\0';
+ switch(len) {
+ case UTC_TIME_STRLEN:
+ {
+ /* infer century and prepend to output */
+ char tmp[3];
+ int year;
+ tmp[0] = inStr[0];
+ tmp[1] = inStr[1];
+ tmp[2] = '\0';
+ year = atoi(tmp);
+
+ /*
+ * 0 <= year < 50 : assume century 21
+ * 50 <= year < 70 : illegal per PKIX
+ * 70 < year <= 99 : assume century 20
+ */
+ if(year < 50) {
+ /* century 21 */
+ strcpy(rtn, "20");
+ }
+ else if(year < 70) {
+ free(rtn);
+ return NULL;
+ }
+ else {
+ /* century 20 */
+ strcpy(rtn, "19");
+ }
+ memmove(rtn + 2, inStr, len - 1); // don't copy the Z
+ break;
+ }
+ case CSSM_TIME_STRLEN:
+ memmove(rtn, inStr, len); // trivial case
+ break;
+ case GENERALIZED_TIME_STRLEN:
+ memmove(rtn, inStr, len - 1); // don't copy the Z
+ break;
+
+ default:
+ free(rtn);
+ return NULL;
+ }
+ rtn[CSSM_TIME_STRLEN] = '\0';
+ *rtnLen = CSSM_TIME_STRLEN;
+ return rtn;
+}
+