📄 fe-secure.c
字号:
libpq_gettext( "server common name \"%s\" does not resolve to %ld.%ld.%ld.%ld\n"), conn->peer_cn, (l >> 24) % 0x100, (l >> 16) % 0x100, (l >> 8) % 0x100, l % 0x100); break; default: printfPQExpBuffer(&conn->errorMessage, libpq_gettext( "server common name \"%s\" does not resolve to peer address\n"), conn->peer_cn); } return -1;}#endif/* * Load precomputed DH parameters. * * To prevent "downgrade" attacks, we perform a number of checks * to verify that the DBA-generated DH parameters file contains * what we expect it to contain. */static DH *load_dh_file(int keylength){#ifdef WIN32 return NULL;#else char pwdbuf[BUFSIZ]; struct passwd pwdstr; struct passwd *pwd = NULL; FILE *fp; char fnbuf[2048]; DH *dh = NULL; int codes; if (pqGetpwuid(getuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) == 0) return NULL; /* attempt to open file. It's not an error if it doesn't exist. */ snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/dh%d.pem", pwd->pw_dir, keylength); if ((fp = fopen(fnbuf, "r")) == NULL) return NULL;/* flock(fileno(fp), LOCK_SH); */ dh = PEM_read_DHparams(fp, NULL, NULL, NULL);/* flock(fileno(fp), LOCK_UN); */ fclose(fp); /* is the prime the correct size? */ if (dh != NULL && 8 * DH_size(dh) < keylength) dh = NULL; /* make sure the DH parameters are usable */ if (dh != NULL) { if (DH_check(dh, &codes)) return NULL; if (codes & DH_CHECK_P_NOT_PRIME) return NULL; if ((codes & DH_NOT_SUITABLE_GENERATOR) && (codes & DH_CHECK_P_NOT_SAFE_PRIME)) return NULL; } return dh;#endif}/* * Load hardcoded DH parameters. * * To prevent problems if the DH parameters files don't even * exist, we can load DH parameters hardcoded into this file. */static DH *load_dh_buffer(const char *buffer, size_t len){ BIO *bio; DH *dh = NULL; bio = BIO_new_mem_buf((char *) buffer, len); if (bio == NULL) return NULL; dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); BIO_free(bio); return dh;}/* * Generate an empheral DH key. Because this can take a long * time to compute, we can use precomputed parameters of the * common key sizes. * * Since few sites will bother to precompute these parameter * files, we also provide a fallback to the parameters provided * by the OpenSSL project. * * These values can be static (once loaded or computed) since * the OpenSSL library can efficiently generate random keys from * the information provided. */static DH *tmp_dh_cb(SSL *s, int is_export, int keylength){ DH *r = NULL; static DH *dh = NULL; static DH *dh512 = NULL; static DH *dh1024 = NULL; static DH *dh2048 = NULL; static DH *dh4096 = NULL; switch (keylength) { case 512: if (dh512 == NULL) dh512 = load_dh_file(keylength); if (dh512 == NULL) dh512 = load_dh_buffer(file_dh512, sizeof file_dh512); r = dh512; break; case 1024: if (dh1024 == NULL) dh1024 = load_dh_file(keylength); if (dh1024 == NULL) dh1024 = load_dh_buffer(file_dh1024, sizeof file_dh1024); r = dh1024; break; case 2048: if (dh2048 == NULL) dh2048 = load_dh_file(keylength); if (dh2048 == NULL) dh2048 = load_dh_buffer(file_dh2048, sizeof file_dh2048); r = dh2048; break; case 4096: if (dh4096 == NULL) dh4096 = load_dh_file(keylength); if (dh4096 == NULL) dh4096 = load_dh_buffer(file_dh4096, sizeof file_dh4096); r = dh4096; break; default: if (dh == NULL) dh = load_dh_file(keylength); r = dh; } /* this may take a long time, but it may be necessary... */ if (r == NULL || 8 * DH_size(r) < keylength) r = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL); return r;}/* * Callback used by SSL to load client cert and key. * This callback is only called when the server wants a * client cert. * * Returns 1 on success, 0 on no data, -1 on error. */static intclient_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey){#ifdef WIN32 return 0;#else char pwdbuf[BUFSIZ]; struct passwd pwdstr; struct passwd *pwd = NULL; struct stat buf, buf2; char fnbuf[2048]; FILE *fp; PGconn *conn = (PGconn *) SSL_get_app_data(ssl); int (*cb) () = NULL; /* how to read user password */ char sebuf[256]; if (pqGetpwuid(getuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) == 0) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not get user information\n")); return -1; } /* read the user certificate */ snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/postgresql.crt", pwd->pw_dir); if (stat(fnbuf, &buf) == -1) return 0; if ((fp = fopen(fnbuf, "r")) == NULL) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not open certificate (%s): %s\n"), fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf))); return -1; } if (PEM_read_X509(fp, x509, NULL, NULL) == NULL) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not read certificate (%s): %s\n"), fnbuf, SSLerrmessage()); fclose(fp); return -1; } fclose(fp); /* read the user key */ snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/postgresql.key", pwd->pw_dir); if (stat(fnbuf, &buf) == -1) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("certificate present, but not private key (%s)\n"), fnbuf); X509_free(*x509); return 0; } if (!S_ISREG(buf.st_mode) || (buf.st_mode & 0077) || buf.st_uid != getuid()) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("private key (%s) has wrong permissions\n"), fnbuf); X509_free(*x509); return -1; } if ((fp = fopen(fnbuf, "r")) == NULL) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not open private key file (%s): %s\n"), fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf))); X509_free(*x509); return -1; } if (fstat(fileno(fp), &buf2) == -1 || buf.st_dev != buf2.st_dev || buf.st_ino != buf2.st_ino) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("private key (%s) changed during execution\n"), fnbuf); X509_free(*x509); return -1; } if (PEM_read_PrivateKey(fp, pkey, cb, NULL) == NULL) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not read private key (%s): %s\n"), fnbuf, SSLerrmessage()); X509_free(*x509); fclose(fp); return -1; } fclose(fp); /* verify that the cert and key go together */ if (!X509_check_private_key(*x509, *pkey)) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("certificate/private key mismatch (%s): %s\n"), fnbuf, SSLerrmessage()); X509_free(*x509); EVP_PKEY_free(*pkey); return -1; } return 1;#endif}/* * Initialize global SSL context. */static intinitialize_SSL(PGconn *conn){#ifndef WIN32 struct stat buf; char pwdbuf[BUFSIZ]; struct passwd pwdstr; struct passwd *pwd = NULL; char fnbuf[2048];#endif if (!SSL_context) { SSL_library_init(); SSL_load_error_strings(); SSL_context = SSL_CTX_new(TLSv1_method()); if (!SSL_context) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not create SSL context: %s\n"), SSLerrmessage()); return -1; } }#ifndef WIN32 if (pqGetpwuid(getuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) == 0) { snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/root.crt", pwd->pw_dir); if (stat(fnbuf, &buf) == -1) { return 0;#ifdef NOT_USED char sebuf[256]; /* CLIENT CERTIFICATES NOT REQUIRED bjm 2002-09-26 */ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not read root certificate list (%s): %s\n"), fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf))); return -1;#endif } if (!SSL_CTX_load_verify_locations(SSL_context, fnbuf, 0)) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not read root certificate list (%s): %s\n"), fnbuf, SSLerrmessage()); return -1; } } SSL_CTX_set_verify(SSL_context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb); SSL_CTX_set_verify_depth(SSL_context, 1); /* 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);#endif 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) { switch (SSL_get_error(conn->ssl, r)) { 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: printfPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL error: %s\n"), SSLerrmessage()); close_SSL(conn); return PGRES_POLLING_FAILED; default: printfPQExpBuffer(&conn->errorMessage, libpq_gettext("unrecognized SSL error code\n")); 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) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("certificate could not be obtained: %s\n"), SSLerrmessage()); 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 const char *SSLerrmessage(void){ unsigned long errcode; const char *errreason; static char errbuf[32]; errcode = ERR_get_error(); if (errcode == 0) return "No SSL error reported"; errreason = ERR_reason_error_string(errcode); if (errreason != NULL) return errreason; snprintf(errbuf, sizeof(errbuf), "SSL error code %lu", errcode); return errbuf;}/* * Return pointer to SSL object. */SSL *PQgetssl(PGconn *conn){ if (!conn) return NULL; return conn->ssl;}#endif /* USE_SSL */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -