5e440312f10786dcc86c21399d3e2df1683436d4 galt Wed Aug 5 09:41:40 2015 -0700 adding ssl params crl and crlPath. also more cleanup, got rid of a separate function sqlProfileNew() since it was no longer needed. diff --git src/hg/lib/jksql.c src/hg/lib/jksql.c index 3179ca6..c7b9954 100644 --- src/hg/lib/jksql.c +++ src/hg/lib/jksql.c @@ -50,33 +50,35 @@ struct sqlProfile /* a configuration profile for connecting to a server */ { struct sqlProfile *next; char *name; // name of profile char *host; // host name for database server unsigned int port; // port for database server char *socket; // unix-domain socket path for database server char *user; // database server user name char *password; // database server password struct slName *dbs; // database associated with profile, can be NULL. // ssl char *key; // path to ssl client key.pem char *cert; // path to ssl client cert.pem char *ca; // path to ssl certificate authority ca.pem - char *caPath; // path to directory containing ssl .pem certs + char *caPath; // path to directory containing ssl .pem certs (only OpenSSL) char *cipher; // list of permissible ciphers to use - char *verifyServerCert; // Client will check server cert Subject CN={host}. + char *crl; // path to file containing certificate revocation lists in PEM format + char *crlPath; // path to directory containing crl files (only OpenSSL) + char *verifyServerCert; // Client will check server cert Subject CN={host} // Boolean connection flag, if NON-NULL and != "0" then it is on. }; struct sqlConnection /* This is an item on a list of sql open connections. */ { MYSQL *conn; /* Connection. Can be NULL if not connected yet. */ struct sqlProfile *profile; /* profile, or NULL if not opened via a profile */ struct dlNode *node; /* Pointer to list node. */ struct dlList *resultList; /* Any open results. */ boolean hasHardLock; /* TRUE if table has a non-advisory lock. */ boolean inCache; /* debugging flag to indicate it's in a cache */ boolean isFree; /* is this connection free for reuse; alway FALSE * unless managed by a cache */ int hasTableCache; /* to avoid repeated checks for cache table name existence, @@ -110,68 +112,48 @@ char *query, ResGetter *getter, boolean abort); static void sqlConnectIfUnconnected(struct sqlConnection *sc, bool abort); bool sqlConnMustUseFailover(struct sqlConnection *sc); static char *envOverride(char *envName, char *defaultVal) /* look up envName in environment, if it exists and is non-empty, return its * value, otherwise return defaultVal */ { char *val = getenv(envName); if (isEmpty(val)) return defaultVal; else return val; } -struct sqlProfile *sqlProfileNew(char *name, char *host, unsigned int port, - char *socket, char *user, char *password, - char *key, char *cert, char *ca, char *caPath, char *cipher, char *verifyServerCert) -/* create a new profile object (does not include ->dbs) */ -{ -struct sqlProfile *sp; -AllocVar(sp); -sp->name = cloneString(name); -sp->host = cloneString(host); -sp->port = port; -sp->socket = cloneString(socket); -sp->user = cloneString(user); -sp->password = cloneString(password); -sp->key = cloneString(key); -sp->cert = cloneString(cert); -sp->ca = cloneString(ca); -sp->caPath = cloneString(caPath); -sp->cipher = cloneString(cipher); -sp->verifyServerCert = cloneString(verifyServerCert); -return sp; -} - static struct sqlProfile *sqlProfileClone(struct sqlProfile *o) /* clone profile object (does not include ->dbs) */ { struct sqlProfile *sp; AllocVar(sp); sp->name = cloneString(o->name); sp->host = cloneString(o->host); sp->port = o->port; sp->socket = cloneString(o->socket); sp->user = cloneString(o->user); sp->password = cloneString(o->password); sp->key = cloneString(o->key); sp->cert = cloneString(o->cert); sp->ca = cloneString(o->ca); sp->caPath = cloneString(o->caPath); sp->cipher = cloneString(o->cipher); +sp->crl = cloneString(o->crl); +sp->crlPath = cloneString(o->crlPath); sp->verifyServerCert = cloneString(o->verifyServerCert); return sp; } struct sqlProfile *sqlProfileFromPairs(struct slPair *pairs) /* create a new profile object (does not include ->dbs) */ { struct sqlProfile *sp; AllocVar(sp); struct slPair *p; for(p=pairs; p; p=p->next) { char *value = (char *)p->val; if (sameString(p->name,"name")) sp->name = cloneString(value); @@ -183,30 +165,34 @@ sp->socket = cloneString(value); if (sameString(p->name,"user")) sp->user = cloneString(value); if (sameString(p->name,"password")) sp->password = cloneString(value); if (sameString(p->name,"key")) sp->key = cloneString(value); if (sameString(p->name,"cert")) sp->cert = cloneString(value); if (sameString(p->name,"ca")) sp->ca = cloneString(value); if (sameString(p->name,"caPath")) sp->caPath = cloneString(value); if (sameString(p->name,"cipher")) sp->cipher = cloneString(value); + if (sameString(p->name,"crl")) + sp->crl = cloneString(value); + if (sameString(p->name,"crlPath")) + sp->crlPath = cloneString(value); if (sameString(p->name,"verifyServerCert")) sp->verifyServerCert = cloneString(value); } return sp; } static void sqlProfileAssocDb(struct sqlProfile *sp, char *db) /* associate a db with a profile. If it is already associated with this * profile, don't do anything.*/ { struct sqlProfile *sp2 = hashFindVal(dbToProfile, db); if ((sp2 != NULL) && (sp2 != sp)) errAbort("databases %s already associated with profile %s, trying to associated it with %s", db, sp2->name, sp->name); @@ -228,57 +214,76 @@ static void sqlProfileAddProfIf(char *profileName) /* check if a config prefix is a profile, and if so, add a * sqlProfile object for it if doesn't already exist. */ { char *host = cfgOption2(profileName, "host"); char *portstr = cfgOption2(profileName, "port"); char *socket = cfgOption2(profileName, "socket"); char *user = cfgOption2(profileName, "user"); char *password = cfgOption2(profileName, "password"); // ssl char *key = cfgOption2(profileName, "key"); char *cert = cfgOption2(profileName, "cert"); char *ca = cfgOption2(profileName, "ca"); char *caPath = cfgOption2(profileName, "caPath"); char *cipher = cfgOption2(profileName, "cipher"); +char *crl = cfgOption2(profileName, "crl"); +char *crlPath = cfgOption2(profileName, "crlPath"); char *verifyServerCert = cfgOption2(profileName, "verifyServerCert"); unsigned int port = 0; if ((host != NULL) && (user != NULL) && (password != NULL) && (hashLookup(profiles, profileName) == NULL)) { /* for the default profile, allow environment variable override */ if (sameString(profileName, defaultProfileName)) { host = envOverride("HGDB_HOST", host); portstr = envOverride("HGDB_PORT", portstr); socket = envOverride("HGDB_SOCKET", socket); user = envOverride("HGDB_USER", user); password = envOverride("HGDB_PASSWORD", password); // ssl key = envOverride("HGDB_KEY", key); cert = envOverride("HGDB_CERT", cert); ca = envOverride("HGDB_CA", ca); caPath = envOverride("HGDB_CAPATH", caPath); cipher = envOverride("HGDB_CIPHER", cipher); - verifyServerCert = envOverride("HGDB_CIPHER", verifyServerCert); + crl = envOverride("HGDB_CRL", crl); + crlPath = envOverride("HGDB_CRLPATH", crlPath); + verifyServerCert = envOverride("HGDB_VERIFY_SERVER_CERT", verifyServerCert); } if (portstr != NULL) port = atoi(portstr); - struct sqlProfile *sp = sqlProfileNew(profileName, host, port, socket, user, password, key, cert, ca, caPath, cipher, verifyServerCert); + struct sqlProfile *sp; + AllocVar(sp); + sp->name = cloneString(profileName); + sp->host = cloneString(host); + sp->port = port; + sp->socket = cloneString(socket); + sp->user = cloneString(user); + sp->password = cloneString(password); + sp->key = cloneString(key); + sp->cert = cloneString(cert); + sp->ca = cloneString(ca); + sp->caPath = cloneString(caPath); + sp->cipher = cloneString(cipher); + sp->crl = cloneString(crl); + sp->crlPath = cloneString(crlPath); + sp->verifyServerCert = cloneString(verifyServerCert); sqlProfileCreate(sp); } } static void sqlProfileAddProfs(struct slName *cnames) /* load the profiles from list of config names */ { struct slName *cname; for (cname = cnames; cname != NULL; cname = cname->next) { char *dot1 = strchr(cname->name, '.'); // first dot in name if ((dot1 != NULL) && sameString(dot1, ".host")) { *dot1 = '\0'; sqlProfileAddProfIf(cname->name); @@ -435,30 +440,32 @@ { struct sqlProfile *spIn = sqlProfileFromPairs(pairs); struct sqlProfile *sp = sqlProfileGet(spIn->name, NULL); if (sp == NULL) return sqlProfileCreate(spIn); replaceStr(&sp->host, spIn->host); replaceStr(&sp->socket, spIn->socket); sp->port = spIn->port; replaceStr(&sp->user, spIn->user); replaceStr(&sp->password, spIn->password); replaceStr(&sp->key, spIn->key); replaceStr(&sp->cert, spIn->cert); replaceStr(&sp->ca, spIn->ca); replaceStr(&sp->caPath, spIn->caPath); replaceStr(&sp->cipher, spIn->cipher); +replaceStr(&sp->crl, spIn->crl); +replaceStr(&sp->crlPath, spIn->crlPath); replaceStr(&sp->verifyServerCert, spIn->verifyServerCert); } void sqlProfileConfigDefault(struct slPair *pairs) /* Set configuration for the default profile. This overrides an existing * profile in hg.conf or defines a new one. Results are unpredictable if a * connect cache has been established for this profile. */ { struct slPair *found = slPairFind(pairs, "name"); if (found) found->val = defaultProfileName; else slPairAdd(&pairs, "name", defaultProfileName); sqlProfileConfig(pairs); } @@ -480,30 +487,37 @@ dyStringPrintf(dy, "password=%s\n", sp->password); if (sp->port) dyStringPrintf(dy, "port=%d\n", sp->port); if (sp->socket) dyStringPrintf(dy, "socket=%s\n", sp->socket); if (sp->key) dyStringPrintf(dy, "ssl-key=%s\n", sp->key); if (sp->cert) dyStringPrintf(dy, "ssl-cert=%s\n", sp->cert); if (sp->ca) dyStringPrintf(dy, "ssl-ca=%s\n", sp->ca); if (sp->caPath) dyStringPrintf(dy, "ssl-capath=%s\n", sp->caPath); if (sp->cipher) dyStringPrintf(dy, "ssl-cipher=%s\n", sp->cipher); +if (mysql_get_client_version() >= 50603) // mysql version "5.6.3" + { + if (sp->crl) + dyStringPrintf(dy, "ssl-crl=%s\n", sp->crl); + if (sp->crlPath) + dyStringPrintf(dy, "ssl-crlpath=%s\n", sp->crlPath); + } if (sp->verifyServerCert && !sameString(sp->verifyServerCert,"0")) dyStringPrintf(dy, "ssl-verify-server-cert\n"); return dyStringCannibalize(&dy); } static void monitorInit(void) /* initialize monitoring on the first call */ { unsigned flags = 0; char *val; /* there is special code in cheap.cgi to pass these from cgiOption to env */ val = getenv("JKSQL_TRACE"); @@ -1057,31 +1071,41 @@ if ((sc->conn = conn = mysql_init(NULL)) == NULL) // no need for monitorLeave here errAbort("Couldn't connect to mySQL."); // Fix problem where client LOCAL setting is disabled by default for security mysql_options(conn, MYSQL_OPT_LOCAL_INFILE, NULL); // Boolean option to tell client to verify that the host server certificate Subject CN equals the hostname. // If turned on this can defeat Man-In-The-Middle attacks. if (sp->verifyServerCert && !sameString(sp->verifyServerCert,"0")) { my_bool flag = TRUE; mysql_options(conn, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &flag); } -// TODO GALT should be optional based on some mysql variable setting? +if (mysql_get_client_version() >= 50603) // mysql version "5.6.3" + { + // If certificate revocation list file provided, set mysql option + if (sp->crl) + mysql_options(conn, MYSQL_OPT_SSL_CRL, &sp->crl); + + // If path to directory with crl files provided, set mysql option + if (sp->crlPath) + mysql_options(conn, MYSQL_OPT_SSL_CRLPATH, &sp->crlPath); + } + if (sp->key || sp->cert || sp->ca || sp->caPath || sp->cipher) mysql_ssl_set(conn, sp->key, sp->cert, sp->ca, sp->caPath, sp->cipher); if (mysql_real_connect( conn, sp->host, /* host */ sp->user, /* user name */ sp->password, /* password */ database, /* database */ sp->port, /* port */ sp->socket, /* socket */ 0) /* flags */ == NULL) { monitorLeave(); monitorEnterTime = oldTime; @@ -1124,50 +1148,50 @@ /* Connect to database somewhere as somebody. Database maybe NULL to just * connect to the server. If abort is set display error message and abort on * error. */ { struct sqlConnection *sc; AllocVar(sc); return sqlConnRemoteFillIn(sc, sp, database, abort, TRUE); } struct sqlConnection *sqlConnectRemote(char *host, char *user, char *password, char *database) /* Connect to database somewhere as somebody. Database maybe NULL to * just connect to the server. Abort on error. * This only takes limited connection parameters. Use Full version for access to all.*/ { -struct sqlProfile* sp = sqlProfileNew( - NULL, - host, 0, NULL, user, password, - NULL, NULL, NULL, NULL, NULL, NULL // ssl params - ); +struct sqlProfile *sp; +AllocVar(sp); +sp->host = cloneString(host); +sp->user = cloneString(user); +sp->password = cloneString(password); return sqlConnRemote(sp, database, TRUE); } struct sqlConnection *sqlMayConnectRemote(char *host, char *user, char *password, char *database) /* Connect to database somewhere as somebody. Database maybe NULL to * just connect to the server. Return NULL if can't connect. * This only takes limited connection parameters. Use Full version for access to all.*/ { -struct sqlProfile* sp = sqlProfileNew( - NULL, - host, 0, NULL, user, password, - NULL, NULL, NULL, NULL, NULL, NULL // ssl params - ); +struct sqlProfile *sp; +AllocVar(sp); +sp->host = cloneString(host); +sp->user = cloneString(user); +sp->password = cloneString(password); return sqlConnRemote(sp, database, FALSE); } struct sqlConnection *sqlConnectRemoteFull(struct slPair *pairs, char *database) /* Connect to database somewhere as somebody. Database maybe NULL to * just connect to the server. Abort on error. * Connection parameter pairs contains a list of name/values. */ { struct sqlProfile *sp = sqlProfileFromPairs(pairs); return sqlConnRemote(sp, database, TRUE); } struct sqlConnection *sqlMayConnectRemoteFull(struct slPair *pairs, char *database) /* Connect to database somewhere as somebody. Database maybe NULL to * just connect to the server.