📄 fe-secure.c
字号:
* shouldn't be cast to unsigned long, but CRYPTO_set_id_callback requires * it, so we have to do it. */ return (unsigned long) pthread_self();}static pthread_mutex_t *pq_lockarray;static voidpq_lockingcallback(int mode, int n, const char *file, int line){ if (mode & CRYPTO_LOCK) pthread_mutex_lock(&pq_lockarray[n]); else pthread_mutex_unlock(&pq_lockarray[n]);}#endif /* ENABLE_THREAD_SAFETY */static intinit_ssl_system(PGconn *conn){#ifdef ENABLE_THREAD_SAFETY#ifndef WIN32 static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;#else static pthread_mutex_t init_mutex = NULL; static long mutex_initlock = 0; if (init_mutex == NULL) { while (InterlockedExchange(&mutex_initlock, 1) == 1) /* loop, another thread own the lock */ ; if (init_mutex == NULL) pthread_mutex_init(&init_mutex, NULL); InterlockedExchange(&mutex_initlock, 0); }#endif pthread_mutex_lock(&init_mutex); if (pq_initssllib && pq_lockarray == NULL) { int i; CRYPTO_set_id_callback(pq_threadidcallback); pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks()); if (!pq_lockarray) { pthread_mutex_unlock(&init_mutex); return -1; } for (i = 0; i < CRYPTO_num_locks(); i++) pthread_mutex_init(&pq_lockarray[i], NULL); CRYPTO_set_locking_callback(pq_lockingcallback); }#endif if (!SSL_context) { if (pq_initssllib) { SSL_library_init(); SSL_load_error_strings(); } SSL_context = SSL_CTX_new(TLSv1_method()); if (!SSL_context) { char *err = SSLerrmessage(); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not create SSL context: %s\n"), err); SSLerrfree(err);#ifdef ENABLE_THREAD_SAFETY pthread_mutex_unlock(&init_mutex);#endif return -1; } }#ifdef ENABLE_THREAD_SAFETY pthread_mutex_unlock(&init_mutex);#endif return 0;}/* * Initialize global SSL context. */static intinitialize_SSL(PGconn *conn){ struct stat buf; char homedir[MAXPGPATH]; char fnbuf[MAXPGPATH]; if (init_ssl_system(conn)) return -1; /* Set up to verify server cert, if root.crt is present */ if (pqGetHomeDirectory(homedir, sizeof(homedir))) { snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOTCERTFILE); if (stat(fnbuf, &buf) == 0) { if (!SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL)) { char *err = SSLerrmessage(); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not read root certificate file \"%s\": %s\n"), fnbuf, err); SSLerrfree(err); return -1; } SSL_CTX_set_verify(SSL_context, SSL_VERIFY_PEER, verify_cb); } } /* set up empheral DH keys */ SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb); SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_DH_USE); /* set up mechanism to provide client certificate, if available */ SSL_CTX_set_client_cert_cb(SSL_context, client_cert_cb); return 0;}/* * Destroy global SSL context. */static voiddestroy_SSL(void){ if (SSL_context) { SSL_CTX_free(SSL_context); SSL_context = NULL; }}/* * Attempt to negotiate SSL connection. */static PostgresPollingStatusTypeopen_client_SSL(PGconn *conn){ int r; r = SSL_connect(conn->ssl); if (r <= 0) { int err = SSL_get_error(conn->ssl, r); switch (err) { case SSL_ERROR_WANT_READ: return PGRES_POLLING_READING; case SSL_ERROR_WANT_WRITE: return PGRES_POLLING_WRITING; case SSL_ERROR_SYSCALL: { char sebuf[256]; if (r == -1) printfPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL SYSCALL error: %s\n"), SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); else printfPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL SYSCALL error: EOF detected\n")); close_SSL(conn); return PGRES_POLLING_FAILED; } case SSL_ERROR_SSL: { /* * If there are problems with the local certificate files, * these will be detected by client_cert_cb() which is * called from SSL_connect(). We want to return that * error message and not the rather unhelpful error that * OpenSSL itself returns. So check to see if an error * message was already stored. */ if (conn->errorMessage.len == 0) { char *err = SSLerrmessage(); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL error: %s\n"), err); SSLerrfree(err); } close_SSL(conn); return PGRES_POLLING_FAILED; } default: printfPQExpBuffer(&conn->errorMessage, libpq_gettext("unrecognized SSL error code: %d\n"), err); close_SSL(conn); return PGRES_POLLING_FAILED; } } /* check the certificate chain of the server */#ifdef NOT_USED /* CLIENT CERTIFICATES NOT REQUIRED bjm 2002-09-26 */ /* * this eliminates simple man-in-the-middle attacks and simple * impersonations */ r = SSL_get_verify_result(conn->ssl); if (r != X509_V_OK) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("certificate could not be validated: %s\n"), X509_verify_cert_error_string(r)); close_SSL(conn); return PGRES_POLLING_FAILED; }#endif /* pull out server distinguished and common names */ conn->peer = SSL_get_peer_certificate(conn->ssl); if (conn->peer == NULL) { char *err = SSLerrmessage(); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("certificate could not be obtained: %s\n"), err); SSLerrfree(err); close_SSL(conn); return PGRES_POLLING_FAILED; } X509_NAME_oneline(X509_get_subject_name(conn->peer), conn->peer_dn, sizeof(conn->peer_dn)); conn->peer_dn[sizeof(conn->peer_dn) - 1] = '\0'; X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer), NID_commonName, conn->peer_cn, SM_USER); conn->peer_cn[SM_USER] = '\0'; /* verify that the common name resolves to peer */#ifdef NOT_USED /* CLIENT CERTIFICATES NOT REQUIRED bjm 2002-09-26 */ /* * this is necessary to eliminate man-in-the-middle attacks and * impersonations where the attacker somehow learned the server's private * key */ if (verify_peer(conn) == -1) { close_SSL(conn); return PGRES_POLLING_FAILED; }#endif /* SSL handshake is complete */ return PGRES_POLLING_OK;}/* * Close SSL connection. */static voidclose_SSL(PGconn *conn){ if (conn->ssl) { SSL_shutdown(conn->ssl); SSL_free(conn->ssl); conn->ssl = NULL; } if (conn->peer) { X509_free(conn->peer); conn->peer = NULL; }}/* * Obtain reason string for last SSL error * * Some caution is needed here since ERR_reason_error_string will * return NULL if it doesn't recognize the error code. We don't * want to return NULL ever. */static char ssl_nomem[] = "Out of memory allocating error description";#define SSL_ERR_LEN 128static char *SSLerrmessage(void){ unsigned long errcode; const char *errreason; char *errbuf; errbuf = malloc(SSL_ERR_LEN); if (!errbuf) return ssl_nomem; errcode = ERR_get_error(); if (errcode == 0) { strcpy(errbuf, "No SSL error reported"); return errbuf; } errreason = ERR_reason_error_string(errcode); if (errreason != NULL) { strncpy(errbuf, errreason, SSL_ERR_LEN - 1); errbuf[SSL_ERR_LEN - 1] = '\0'; return errbuf; } snprintf(errbuf, SSL_ERR_LEN, "SSL error code %lu", errcode); return errbuf;}static voidSSLerrfree(char *buf){ if (buf != ssl_nomem) free(buf);}/* * Return pointer to SSL object. */SSL *PQgetssl(PGconn *conn){ if (!conn) return NULL; return conn->ssl;}#else /* !USE_SSL */void *PQgetssl(PGconn *conn){ return NULL;}#endif /* USE_SSL */#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)/* * Block SIGPIPE for this thread. This prevents send()/write() from exiting * the application. */intpq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending){ sigset_t sigpipe_sigset; sigset_t sigset; sigemptyset(&sigpipe_sigset); sigaddset(&sigpipe_sigset, SIGPIPE); /* Block SIGPIPE and save previous mask for later reset */ SOCK_ERRNO_SET(pthread_sigmask(SIG_BLOCK, &sigpipe_sigset, osigset)); if (SOCK_ERRNO) return -1; /* We can have a pending SIGPIPE only if it was blocked before */ if (sigismember(osigset, SIGPIPE)) { /* Is there a pending SIGPIPE? */ if (sigpending(&sigset) != 0) return -1; if (sigismember(&sigset, SIGPIPE)) *sigpipe_pending = true; else *sigpipe_pending = false; } else *sigpipe_pending = false; return 0;}/* * Discard any pending SIGPIPE and reset the signal mask. * * Note: we are effectively assuming here that the C library doesn't queue * up multiple SIGPIPE events. If it did, then we'd accidentally leave * ours in the queue when an event was already pending and we got another. * As long as it doesn't queue multiple events, we're OK because the caller * can't tell the difference. * * The caller should say got_epipe = FALSE if it is certain that it * didn't get an EPIPE error; in that case we'll skip the clear operation * and things are definitely OK, queuing or no. If it got one or might have * gotten one, pass got_epipe = TRUE. * * We do not want this to change errno, since if it did that could lose * the error code from a preceding send(). We essentially assume that if * we were able to do pq_block_sigpipe(), this can't fail. */voidpq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe){ int save_errno = SOCK_ERRNO; int signo; sigset_t sigset; /* Clear SIGPIPE only if none was pending */ if (got_epipe && !sigpipe_pending) { if (sigpending(&sigset) == 0 && sigismember(&sigset, SIGPIPE)) { sigset_t sigpipe_sigset; sigemptyset(&sigpipe_sigset); sigaddset(&sigpipe_sigset, SIGPIPE); sigwait(&sigpipe_sigset, &signo); } } /* Restore saved block mask */ pthread_sigmask(SIG_SETMASK, osigset, NULL); SOCK_ERRNO_SET(save_errno);}#endif /* ENABLE_THREAD_SAFETY */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -