/*** Copyright (c), The Regents of the University of California *** *** For more information please refer to files in the COPYRIGHT directory ***/ /* sslSockComm.c - SSL socket communication routines */ #ifdef USE_SSL #include "rodsClient.h" #include "sslSockComm.h" /* module internal functions */ static SSL_CTX *sslInit(char *certfile, char *keyfile); static SSL *sslInitSocket(SSL_CTX *ctx, int sock); static void sslLogError(char *msg); static DH *get_dh2048(); static int sslLoadDHParams(SSL_CTX *ctx, char *file); static int sslVerifyCallback(int ok, X509_STORE_CTX *store); static int sslPostConnectionCheck(SSL *ssl, char *peer); int sslStart(rcComm_t *rcComm) { int status; sslStartInp_t sslStartInp; if (rcComm == NULL) { return USER__NULL_INPUT_ERR; } if (rcComm->ssl_on) { /* SSL connection turned on ... return */ return 0; } /* ask the server if we can start SSL */ memset(&sslStartInp, 0, sizeof(sslStartInp)); status = rcSslStart(rcComm, &sslStartInp); if (status < 0) { rodsLogError(LOG_ERROR, status, "sslStart: server refused our request to start SSL"); return (status); } /* we have the go-ahead ... set up SSL on our side of the socket */ rcComm->ssl_ctx = sslInit(NULL, NULL); if (rcComm->ssl_ctx == NULL) { rodsLog(LOG_ERROR, "sslStart: couldn't initialize SSL context"); return SSL_INIT_ERROR; } rcComm->ssl = sslInitSocket(rcComm->ssl_ctx, rcComm->sock); if (rcComm->ssl == NULL) { rodsLog(LOG_ERROR, "sslStart: couldn't initialize SSL socket"); SSL_CTX_free(rcComm->ssl_ctx); rcComm->ssl_ctx = NULL; return SSL_INIT_ERROR; } status = SSL_connect(rcComm->ssl); if (status < 1) { sslLogError("sslStart: error in SSL_connect"); SSL_free(rcComm->ssl); rcComm->ssl = NULL; SSL_CTX_free(rcComm->ssl_ctx); rcComm->ssl_ctx = NULL; return SSL_HANDSHAKE_ERROR; } rcComm->ssl_on = 1; if (!sslPostConnectionCheck(rcComm->ssl, rcComm->host)) { rodsLog(LOG_ERROR, "sslStart: post connection certificate check failed"); sslEnd(rcComm); return SSL_CERT_ERROR; } return 0; } int sslEnd(rcComm_t *rcComm) { int status; sslEndInp_t sslEndInp; if (rcComm == NULL) { return USER__NULL_INPUT_ERR; } if (!rcComm->ssl_on) { /* no SSL connection turned on ... return */ return 0; } memset(&sslEndInp, 0, sizeof(sslEndInp)); status = rcSslEnd(rcComm, &sslEndInp); if (status < 0) { rodsLogError(LOG_ERROR, status, "sslEnd: server refused our request to stop SSL"); return (status); } /* shut down the SSL connection. First SSL_shutdown() sends "close notify" */ status = SSL_shutdown(rcComm->ssl); if (status == 0) { /* do second phase of shutdown */ status = SSL_shutdown(rcComm->ssl); } if (status != 1) { sslLogError("sslEnd: error shutting down the SSL connection"); return SSL_SHUTDOWN_ERROR; } /* clean up the SSL state */ SSL_free(rcComm->ssl); rcComm->ssl = NULL; SSL_CTX_free(rcComm->ssl_ctx); rcComm->ssl_ctx = NULL; rcComm->ssl_on = 0; return 0; } int sslAccept(rsComm_t *rsComm) { int status; /* set up the context using a certificate file and separate keyfile passed through environment variables */ rsComm->ssl_ctx = sslInit(getenv("irodsSSLCertificateChainFile"), getenv("irodsSSLCertificateKeyFile")); if (rsComm->ssl_ctx == NULL) { rodsLog(LOG_ERROR, "sslAccept: couldn't initialize SSL context"); return SSL_INIT_ERROR; } status = sslLoadDHParams(rsComm->ssl_ctx, getenv("irodsSSLDHParamsFile")); if (status < 0) { rodsLog(LOG_ERROR, "sslAccept: error setting Diffie-Hellman parameters"); SSL_CTX_free(rsComm->ssl_ctx); rsComm->ssl_ctx = NULL; return SSL_INIT_ERROR; } rsComm->ssl = sslInitSocket(rsComm->ssl_ctx, rsComm->sock); if (rsComm->ssl == NULL) { rodsLog(LOG_ERROR, "sslAccept: couldn't initialize SSL socket"); SSL_CTX_free(rsComm->ssl_ctx); rsComm->ssl_ctx = NULL; return SSL_INIT_ERROR; } status = SSL_accept(rsComm->ssl); if (status < 1) { sslLogError("sslAccept: error calling SSL_accept"); return SSL_HANDSHAKE_ERROR; } rsComm->ssl_on = 1; rodsLog(LOG_DEBUG, "sslAccept: accepted SSL connection"); return 0; } int sslShutdown(rsComm_t *rsComm) { int status; /* shut down the SSL connection. Might need to call SSL_shutdown() twice to allow the protocol to notify and then complete the shutdown. */ status = SSL_shutdown(rsComm->ssl); if (status == 0) { /* second phase of shutdown */ status = SSL_shutdown(rsComm->ssl); } if (status != 1) { sslLogError("sslShutdown: error completing shutdown of SSL connection"); return SSL_SHUTDOWN_ERROR; } /* clean up the SSL state */ SSL_free(rsComm->ssl); rsComm->ssl = NULL; SSL_CTX_free(rsComm->ssl_ctx); rsComm->ssl_ctx = NULL; rsComm->ssl_on = 0; rodsLog(LOG_DEBUG, "sslShutdown: shut down SSL connection"); return 0; } int sslReadMsgHeader (int sock, msgHeader_t *myHeader, struct timeval *tv, SSL *ssl) { int nbytes; int myLen; char tmpBuf[MAX_NAME_LEN]; msgHeader_t *outHeader; int status; /* read the header length packet */ nbytes = sslRead(sock, (void *) &myLen, sizeof (myLen), SOCK_TYPE, NULL, tv, ssl); if (nbytes != sizeof (myLen)) { if (nbytes < 0) { status = nbytes - errno; } else { status = SYS_HEADER_READ_LEN_ERR - errno; } rodsLog (LOG_ERROR, "sslReadMsgHeader:header read- read %d bytes, expect %d, status = %d", nbytes, sizeof (myLen), status); return (status); } myLen = ntohl (myLen); if (myLen > MAX_NAME_LEN || myLen <= 0) { rodsLog (LOG_ERROR, "sslReadMsgHeader: header length %d out of range", myLen); return (SYS_HEADER_READ_LEN_ERR); } nbytes = sslRead(sock, (void *) tmpBuf, myLen, SOCK_TYPE, NULL, tv, ssl); if (nbytes != myLen) { if (nbytes < 0) { status = nbytes - errno; } else { status = SYS_HEADER_READ_LEN_ERR - errno; } rodsLog (LOG_ERROR, "sslReadMsgHeader:header read- read %d bytes, expect %d, status = %d", nbytes, myLen, status); return (status); } if (getRodsLogLevel () >= LOG_DEBUG3) { printf ("received header: len = %d\n%s\n", myLen, tmpBuf); } /* always use XML_PROT for the startup pack */ status = unpackStruct ((void *) tmpBuf, (void **) &outHeader, "MsgHeader_PI", RodsPackTable, XML_PROT); if (status < 0) { rodsLogError (LOG_ERROR, status, "sslReadMsgHeader:unpackStruct error. status = %d", status); return (status); } *myHeader = *outHeader; free (outHeader); return (0); } int sslReadMsgBody (int sock, msgHeader_t *myHeader, bytesBuf_t *inputStructBBuf, bytesBuf_t *bsBBuf, bytesBuf_t *errorBBuf, irodsProt_t irodsProt, struct timeval *tv, SSL *ssl) { int nbytes; int bytesRead; if (myHeader == NULL) { return (SYS_READ_MSG_BODY_INPUT_ERR); } if (inputStructBBuf != NULL) memset (inputStructBBuf, 0, sizeof (bytesBuf_t)); /* Don't memset bsBBuf because bsBBuf can be reused on the client side */ if (errorBBuf != NULL) memset (errorBBuf, 0, sizeof (bytesBuf_t)); if (myHeader->msgLen > 0) { if (inputStructBBuf == NULL) { return (SYS_READ_MSG_BODY_INPUT_ERR); } inputStructBBuf->buf = malloc (myHeader->msgLen); nbytes = sslRead(sock, inputStructBBuf->buf, myHeader->msgLen, SOCK_TYPE, NULL, tv, ssl); if (irodsProt == XML_PROT && getRodsLogLevel () >= LOG_DEBUG3) { printf ("received msg: \n%s\n", (char *) inputStructBBuf->buf); } if (nbytes != myHeader->msgLen) { rodsLog (LOG_NOTICE, "sslReadMsgBody: inputStruct read error, read %d bytes, expect %d", nbytes, myHeader->msgLen); free (inputStructBBuf->buf); return (SYS_HEADER_READ_LEN_ERR); } inputStructBBuf->len = myHeader->msgLen; } if (myHeader->errorLen > 0) { if (errorBBuf == NULL) { return (SYS_READ_MSG_BODY_INPUT_ERR); } errorBBuf->buf = malloc (myHeader->errorLen); nbytes = sslRead(sock, errorBBuf->buf, myHeader->errorLen, SOCK_TYPE, NULL, tv, ssl); if (irodsProt == XML_PROT && getRodsLogLevel () >= LOG_DEBUG3) { printf ("received error msg: \n%s\n", (char *) errorBBuf->buf); } if (nbytes != myHeader->errorLen) { rodsLog (LOG_NOTICE, "sslReadMsgBody: errorBbuf read error, read %d bytes, expect %d, errno = %d", nbytes, myHeader->msgLen, errno); free (errorBBuf->buf); return (SYS_READ_MSG_BODY_LEN_ERR - errno); } errorBBuf->len = myHeader->errorLen; } if (myHeader->bsLen > 0) { if (bsBBuf == NULL) { return (SYS_READ_MSG_BODY_INPUT_ERR); } if (bsBBuf->buf == NULL) { bsBBuf->buf = malloc (myHeader->bsLen); } else if (myHeader->bsLen > bsBBuf->len) { free (bsBBuf->buf); bsBBuf->buf = malloc (myHeader->bsLen); } nbytes = sslRead(sock, bsBBuf->buf, myHeader->bsLen, SOCK_TYPE, &bytesRead, tv, ssl); if (nbytes != myHeader->bsLen) { rodsLog (LOG_NOTICE, "sslReadMsgBody: bsBBuf read error, read %d bytes, expect %d, errno = %d", nbytes, myHeader->bsLen, errno); free (bsBBuf->buf); return (SYS_READ_MSG_BODY_INPUT_ERR - errno); } bsBBuf->len = myHeader->bsLen; } return (0); } int sslWriteMsgHeader (int sock, msgHeader_t *myHeader, SSL *ssl) { int nbytes; int status; int myLen; bytesBuf_t *headerBBuf = NULL; /* always use XML_PROT for the Header */ status = packStruct ((void *) myHeader, &headerBBuf, "MsgHeader_PI", RodsPackTable, 0, XML_PROT); if (status < 0) { rodsLogError (LOG_ERROR, status, "sslWriteMsgHeader: packStruct error, status = %d", status); return status; } if (getRodsLogLevel () >= LOG_DEBUG3) { printf ("sending header: len = %d\n%s\n", headerBBuf->len, (char *) headerBBuf->buf); } myLen = htonl (headerBBuf->len); nbytes = sslWrite(sock, (void *) &myLen, sizeof (myLen), SOCK_TYPE, NULL, ssl); if (nbytes != sizeof (myLen)) { rodsLog (LOG_ERROR, "sslWriteMsgHeader: wrote %d bytes for myLen , expect %d, status = %d", nbytes, sizeof (myLen), SYS_HEADER_WRITE_LEN_ERR - errno); return (SYS_HEADER_WRITE_LEN_ERR - errno); } /* now send the header */ nbytes = sslWrite(sock, headerBBuf->buf, headerBBuf->len, SOCK_TYPE, NULL, ssl); if (headerBBuf->len != nbytes) { rodsLog (LOG_ERROR, "sslWriteMsgHeader: wrote %d bytes, expect %d, status = %d", nbytes, headerBBuf->len, SYS_HEADER_WRITE_LEN_ERR - errno); freeBBuf (headerBBuf); return (SYS_HEADER_WRITE_LEN_ERR - errno); } freeBBuf (headerBBuf); return (0); } int sslSendRodsMsg (int sock, char *msgType, bytesBuf_t *msgBBuf, bytesBuf_t *byteStreamBBuf, bytesBuf_t *errorBBuf, int intInfo, irodsProt_t irodsProt, SSL *ssl) { int status; msgHeader_t msgHeader; int bytesWritten; memset (&msgHeader, 0, sizeof (msgHeader)); rstrcpy (msgHeader.type, msgType, HEADER_TYPE_LEN); if (msgBBuf == NULL) { msgHeader.msgLen = 0; } else { msgHeader.msgLen = msgBBuf->len; } if (byteStreamBBuf == NULL) { msgHeader.bsLen = 0; } else { msgHeader.bsLen = byteStreamBBuf->len; } if (errorBBuf == NULL) { msgHeader.errorLen = 0; } else { msgHeader.errorLen = errorBBuf->len; } msgHeader.intInfo = intInfo; status = sslWriteMsgHeader (sock, &msgHeader, ssl); if (status < 0) return (status); /* send the rest */ if (msgHeader.msgLen > 0) { if (irodsProt == XML_PROT && getRodsLogLevel () >= LOG_DEBUG3) { printf ("sending msg: \n%s\n", (char *) msgBBuf->buf); } status = sslWrite(sock, msgBBuf->buf, msgBBuf->len, SOCK_TYPE, NULL, ssl); if (status < 0) return (status); } if (msgHeader.errorLen > 0) { if (irodsProt == XML_PROT && getRodsLogLevel () >= LOG_DEBUG3) { printf ("sending error msg: \n%s\n", (char *) errorBBuf->buf); } status = sslWrite(sock, errorBBuf->buf, errorBBuf->len, SOCK_TYPE, NULL, ssl); if (status < 0) return (status); } if (msgHeader.bsLen > 0) { status = sslWrite(sock, byteStreamBBuf->buf, byteStreamBBuf->len, SOCK_TYPE, &bytesWritten, ssl); if (status < 0) return (status); } return (0); } int sslRead(int sock, void *buf, int len, irodsDescType_t irodsDescType, int *bytesRead, struct timeval *tv, SSL *ssl) { int nbytes; int toRead; char *tmpPtr; fd_set set; struct timeval timeout; int status; /* Initialize the file descriptor set. */ FD_ZERO (&set); FD_SET (sock, &set); if (tv != NULL) timeout = *tv; toRead = len; tmpPtr = (char *) buf; if (bytesRead != NULL) *bytesRead = 0; while (toRead > 0) { if (tv != NULL) { status = select (sock + 1, &set, NULL, NULL, &timeout); if (status == 0) { /* timedout */ if (len - toRead > 0) { return (len - toRead); } else { return SYS_SOCK_READ_TIMEDOUT; } } else if (status < 0) { if (errno == EINTR) { continue; } else { return SYS_SOCK_READ_ERR - errno; } } } nbytes = SSL_read(ssl, (void *) tmpPtr, toRead); if (SSL_get_error(ssl, nbytes) != SSL_ERROR_NONE) { if (errno == EINTR) { /* interrupted */ errno = 0; nbytes = 0; } else { break; } } toRead -= nbytes; tmpPtr += nbytes; if (bytesRead != NULL) *bytesRead += nbytes; } return (len - toRead); } int sslWrite(int sock, void *buf, int len, irodsDescType_t irodsDescType, int *bytesWritten, SSL *ssl) { int nbytes; int toWrite; char *tmpPtr; toWrite = len; tmpPtr = (char *) buf; if (bytesWritten != NULL) *bytesWritten = 0; while (toWrite > 0) { nbytes = SSL_write(ssl, (void *) tmpPtr, toWrite); if (SSL_get_error(ssl, nbytes) != SSL_ERROR_NONE) { if (errno == EINTR) { /* interrupted */ errno = 0; nbytes = 0; } else { break; } } toWrite -= nbytes; tmpPtr += nbytes; if (bytesWritten != NULL) *bytesWritten += nbytes; } return (len - toWrite); } /* Module internal support functions */ static SSL_CTX* sslInit(char *certfile, char *keyfile) { static int init_done = 0; SSL_CTX *ctx; char *ca_path; char *ca_file; char *verify_server; if (!init_done) { SSL_library_init(); SSL_load_error_strings(); init_done = 1; } /* in our test programs we set up a null signal handler for SIGPIPE */ /* signal(SIGPIPE, sslSigpipeHandler); */ ctx = SSL_CTX_new(SSLv23_method()); /* no SSLv2 */ SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_SINGLE_DH_USE); /* load our keys and certificates if provided */ if (certfile) { if (SSL_CTX_use_certificate_chain_file(ctx, certfile) != 1) { sslLogError("sslInit: couldn't read certificate chain file"); SSL_CTX_free(ctx); return NULL; } else { if (SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM) != 1) { sslLogError("sslInit: couldn't read key file"); SSL_CTX_free(ctx); return NULL; } } } /* set up CA paths and files here */ ca_path = getenv("irodsSSLCACertificatePath"); ca_file = getenv("irodsSSLCACertificateFile"); if (ca_path || ca_file) { if (SSL_CTX_load_verify_locations(ctx, ca_file, ca_path) != 1) { sslLogError("sslInit: error loading CA certificate locations"); } } if (SSL_CTX_set_default_verify_paths(ctx) != 1) { sslLogError("sslInit: error loading default CA certificate locations"); } /* Set up the default certificate verification */ /* if "none" is specified, we won't stop the SSL handshake due to certificate error, but will log messages from the verification callback */ verify_server = getenv("irodsSSLVerifyServer"); if (verify_server && !strcmp(verify_server, "none")) { SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, sslVerifyCallback); } else { SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, sslVerifyCallback); } /* default depth is nine ... leave this here in case it needs modification */ SSL_CTX_set_verify_depth(ctx, 9); /* ciphers */ if (SSL_CTX_set_cipher_list(ctx, SSL_CIPHER_LIST) != 1) { sslLogError("sslInit: couldn't set the cipher list (no valid ciphers)"); SSL_CTX_free(ctx); return NULL; } return ctx; } static SSL* sslInitSocket(SSL_CTX *ctx, int sock) { SSL *ssl; BIO *bio; bio = BIO_new_socket(sock, BIO_NOCLOSE); if (bio == NULL) { sslLogError("sslInitSocket: BIO allocation error"); return NULL; } ssl = SSL_new(ctx); if (ssl == NULL) { sslLogError("sslInitSocket: couldn't create a new SSL socket"); BIO_free(bio); return NULL; } SSL_set_bio(ssl, bio, bio); return ssl; } static void sslLogError(char *msg) { unsigned long err; char buf[512]; while ((err = ERR_get_error())) { ERR_error_string_n(err, buf, 512); rodsLog(LOG_ERROR, "%s. SSL error: %s", msg, buf); } } /* This function returns a set of built-in Diffie-Hellman parameters. We could read the parameters from a file instead, but this is a reasonably strong prime. The parameters come from the openssl distribution's 'apps' sub-directory. Comment from the source file is: These are the 2048 bit DH parameters from "Assigned Number for SKIP Protocols" (http://www.skip-vpn.org/spec/numbers.html). See there for how they were generated. */ static DH* get_dh2048() { static unsigned char dh2048_p[]={ 0xF6,0x42,0x57,0xB7,0x08,0x7F,0x08,0x17,0x72,0xA2,0xBA,0xD6, 0xA9,0x42,0xF3,0x05,0xE8,0xF9,0x53,0x11,0x39,0x4F,0xB6,0xF1, 0x6E,0xB9,0x4B,0x38,0x20,0xDA,0x01,0xA7,0x56,0xA3,0x14,0xE9, 0x8F,0x40,0x55,0xF3,0xD0,0x07,0xC6,0xCB,0x43,0xA9,0x94,0xAD, 0xF7,0x4C,0x64,0x86,0x49,0xF8,0x0C,0x83,0xBD,0x65,0xE9,0x17, 0xD4,0xA1,0xD3,0x50,0xF8,0xF5,0x59,0x5F,0xDC,0x76,0x52,0x4F, 0x3D,0x3D,0x8D,0xDB,0xCE,0x99,0xE1,0x57,0x92,0x59,0xCD,0xFD, 0xB8,0xAE,0x74,0x4F,0xC5,0xFC,0x76,0xBC,0x83,0xC5,0x47,0x30, 0x61,0xCE,0x7C,0xC9,0x66,0xFF,0x15,0xF9,0xBB,0xFD,0x91,0x5E, 0xC7,0x01,0xAA,0xD3,0x5B,0x9E,0x8D,0xA0,0xA5,0x72,0x3A,0xD4, 0x1A,0xF0,0xBF,0x46,0x00,0x58,0x2B,0xE5,0xF4,0x88,0xFD,0x58, 0x4E,0x49,0xDB,0xCD,0x20,0xB4,0x9D,0xE4,0x91,0x07,0x36,0x6B, 0x33,0x6C,0x38,0x0D,0x45,0x1D,0x0F,0x7C,0x88,0xB3,0x1C,0x7C, 0x5B,0x2D,0x8E,0xF6,0xF3,0xC9,0x23,0xC0,0x43,0xF0,0xA5,0x5B, 0x18,0x8D,0x8E,0xBB,0x55,0x8C,0xB8,0x5D,0x38,0xD3,0x34,0xFD, 0x7C,0x17,0x57,0x43,0xA3,0x1D,0x18,0x6C,0xDE,0x33,0x21,0x2C, 0xB5,0x2A,0xFF,0x3C,0xE1,0xB1,0x29,0x40,0x18,0x11,0x8D,0x7C, 0x84,0xA7,0x0A,0x72,0xD6,0x86,0xC4,0x03,0x19,0xC8,0x07,0x29, 0x7A,0xCA,0x95,0x0C,0xD9,0x96,0x9F,0xAB,0xD0,0x0A,0x50,0x9B, 0x02,0x46,0xD3,0x08,0x3D,0x66,0xA4,0x5D,0x41,0x9F,0x9C,0x7C, 0xBD,0x89,0x4B,0x22,0x19,0x26,0xBA,0xAB,0xA2,0x5E,0xC3,0x55, 0xE9,0x32,0x0B,0x3B, }; static unsigned char dh2048_g[]={ 0x02, }; DH *dh; if ((dh = DH_new()) == NULL) { return(NULL); } dh->p = BN_bin2bn(dh2048_p, sizeof(dh2048_p), NULL); dh->g = BN_bin2bn(dh2048_g, sizeof(dh2048_g), NULL); if ((dh->p == NULL) || (dh->g == NULL)) { DH_free(dh); return(NULL); } return(dh); } static int sslLoadDHParams(SSL_CTX *ctx, char *file) { DH *dhparams; BIO *bio; if (file) { bio = BIO_new_file(file, "r"); if (bio) { dhparams = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); BIO_free(bio); } } if (dhparams == NULL) { sslLogError("sslLoadDHParams: can't load DH parameter file. Falling back to built-ins."); dhparams = get_dh2048(); if (dhparams == NULL) { rodsLog(LOG_ERROR, "sslLoadDHParams: can't load built-in DH params"); return -1; } } if (SSL_CTX_set_tmp_dh(ctx, dhparams) < 0) { sslLogError("sslLoadDHParams: couldn't set DH parameters"); return -1; } return 0; } static int sslVerifyCallback(int ok, X509_STORE_CTX *store) { char data[256]; /* log any verification problems, even if we'll still accept the cert */ if (!ok) { X509 *cert = X509_STORE_CTX_get_current_cert(store); int depth = X509_STORE_CTX_get_error_depth(store); int err = X509_STORE_CTX_get_error(store); rodsLog(LOG_NOTICE, "sslVerifyCallback: problem with certificate at depth: %i", depth); X509_NAME_oneline(X509_get_issuer_name(cert), data, 256); rodsLog(LOG_NOTICE, "sslVerifyCallback: issuer = %s", data); X509_NAME_oneline(X509_get_subject_name(cert), data, 256); rodsLog(LOG_NOTICE, "sslVerifyCallback: subject = %s", data); rodsLog(LOG_NOTICE, "sslVerifyCallback: err %i:%s", err, X509_verify_cert_error_string(err)); } return ok; } static int sslPostConnectionCheck(SSL *ssl, char *peer) { char *verify_server; X509 *cert; int match = 0; STACK_OF(GENERAL_NAMES) *names; GENERAL_NAME *name; int num_names, i; char *namestr; char cn[256]; verify_server = getenv("irodsSSLVerifyServer"); if (verify_server && strcmp(verify_server, "hostname")) { /* not being asked to verify that the peer hostname is in the certificate. */ return 1; } cert = SSL_get_peer_certificate(ssl); if (cert == NULL) { /* no certificate presented */ return 0; } if (peer == NULL) { /* no hostname passed to verify */ X509_free(cert); return 0; } /* check if the peer name matches any of the subjectAltNames listed in the certificate */ names = (STACK_OF(GENERAL_NAMES)*)X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); num_names = sk_GENERAL_NAMES_num(names); for (i = 0; i < num_names; i++ ) { name = (GENERAL_NAME*)sk_GENERAL_NAMES_value(names, i); if (name->type == GEN_DNS) { namestr = (char*)ASN1_STRING_data(name->d.dNSName); if (!strcasecmp(namestr, peer)) { match = 1; break; } } } sk_GENERAL_NAMES_free(names); /* if no match above, check the common name in the certificate */ if (!match && (X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, cn, 256) != -1)) { cn[255] = 0; if (!strcasecmp(cn, peer)) { match = 1; } else if (cn[0] == '*') { /* wildcard domain */ char *tmp = strchr(peer, '.'); if (tmp && !strcasecmp(tmp, cn+1)) { match = 1; } } } X509_free(cert); if (match) { return 1; } else { return 0; } } #endif /* USE_SSL */