1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713 |
- /*
- * OpenConnect (SSL + DTLS) VPN client
- *
- * Copyright © 2008-2015 Intel Corporation.
- *
- * Author: David Woodhouse <dwmw2@infradead.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * version 2.1, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- */
- #include <config.h>
- #include "openconnect-internal.h"
- #include <openssl/crypto.h>
- #include <openssl/ssl.h>
- #include <openssl/err.h>
- #include <openssl/engine.h>
- #include <openssl/evp.h>
- #include <openssl/rand.h>
- #include <openssl/pkcs12.h>
- #include <openssl/x509v3.h>
- #include <openssl/x509.h>
- #include <openssl/bio.h>
- #include <openssl/ui.h>
- #include <openssl/rsa.h>
- #include <sys/types.h>
- #include <errno.h>
- #include <string.h>
- #include <ctype.h>
- #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
- #define X509_up_ref(x) CRYPTO_add(&(x)->references, 1, CRYPTO_LOCK_X509)
- #define X509_get0_notAfter(x) X509_get_notAfter(x)
- #define EVP_MD_CTX_new EVP_MD_CTX_create
- #define EVP_MD_CTX_free EVP_MD_CTX_destroy
- #define X509_STORE_CTX_get0_chain(ctx) ((ctx)->chain)
- #define X509_STORE_CTX_get0_untrusted(ctx) ((ctx)->untrusted)
- #define X509_STORE_CTX_get0_cert(ctx) ((ctx)->cert)
- typedef int (*X509_STORE_CTX_get_issuer_fn)(X509 **issuer,
- X509_STORE_CTX *ctx, X509 *x);
- #define X509_STORE_CTX_get_get_issuer(ctx) ((ctx)->get_issuer)
- #define OpenSSL_version SSLeay_version
- #define OPENSSL_VERSION SSLEAY_VERSION
- #endif
- static char tls_library_version[32] = "";
- const char *openconnect_get_tls_library_version(void)
- {
- if (!*tls_library_version) {
- strncpy(tls_library_version,
- OpenSSL_version(OPENSSL_VERSION),
- sizeof(tls_library_version));
- tls_library_version[sizeof(tls_library_version)-1]='\0';
- }
- return tls_library_version;
- }
- int can_enable_insecure_crypto(void)
- {
- if (EVP_des_ede3_cbc() == NULL ||
- EVP_rc4() == NULL)
- return -ENOENT;
- return 0;
- }
- int openconnect_sha1(unsigned char *result, void *data, int len)
- {
- EVP_MD_CTX *c = EVP_MD_CTX_new();
- if (!c)
- return -ENOMEM;
- EVP_Digest(data, len, result, NULL, EVP_sha1(), NULL);
- EVP_MD_CTX_free(c);
- return 0;
- }
- int openconnect_sha256(unsigned char *result, void *data, int len)
- {
- EVP_MD_CTX *c = EVP_MD_CTX_new();
- if (!c)
- return -ENOMEM;
- EVP_Digest(data, len, result, NULL, EVP_sha256(), NULL);
- EVP_MD_CTX_free(c);
- return 0;
- }
- int openconnect_md5(unsigned char *result, void *data, int len)
- {
- EVP_MD_CTX *c = EVP_MD_CTX_new();
- if (!c)
- return -ENOMEM;
- EVP_Digest(data, len, result, NULL, EVP_md5(), NULL);
- EVP_MD_CTX_free(c);
- return 0;
- }
- int openconnect_get_peer_cert_DER(struct openconnect_info *vpninfo,
- unsigned char **buf)
- {
- BIO *bp = BIO_new(BIO_s_mem());
- BUF_MEM *certinfo;
- size_t l;
- if (!i2d_X509_bio(bp, vpninfo->peer_cert)) {
- BIO_free(bp);
- return -EIO;
- }
- BIO_get_mem_ptr(bp, &certinfo);
- l = certinfo->length;
- *buf = malloc(l);
- if (!*buf) {
- BIO_free(bp);
- return -ENOMEM;
- }
- memcpy(*buf, certinfo->data, l);
- BIO_free(bp);
- return l;
- }
- int openconnect_random(void *bytes, int len)
- {
- if (RAND_bytes(bytes, len) != 1)
- return -EIO;
- return 0;
- }
- /* Helper functions for reading/writing lines over TLS/DTLS.
- We could use cURL for the HTTP stuff, but it's overkill */
- static int _openconnect_openssl_write(SSL *ssl, int fd, struct openconnect_info *vpninfo, char *buf, size_t len)
- {
- size_t orig_len = len;
- while (len) {
- int done = SSL_write(ssl, buf, len);
- if (done > 0)
- len -= done;
- else {
- int err = SSL_get_error(ssl, done);
- fd_set wr_set, rd_set;
- int maxfd = fd;
- FD_ZERO(&wr_set);
- FD_ZERO(&rd_set);
- if (err == SSL_ERROR_WANT_READ)
- FD_SET(fd, &rd_set);
- else if (err == SSL_ERROR_WANT_WRITE)
- FD_SET(fd, &wr_set);
- else {
- vpn_progress(vpninfo, PRG_ERR, _("Failed to write to TLS/DTLS socket\n"));
- openconnect_report_ssl_errors(vpninfo);
- return -EIO;
- }
- cmd_fd_set(vpninfo, &rd_set, &maxfd);
- select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
- if (is_cancel_pending(vpninfo, &rd_set)) {
- vpn_progress(vpninfo, PRG_ERR, _("TLS/DTLS write cancelled\n"));
- return -EINTR;
- }
- }
- }
- return orig_len;
- }
- static int openconnect_openssl_write(struct openconnect_info *vpninfo, char *buf, size_t len)
- {
- return _openconnect_openssl_write(vpninfo->https_ssl, vpninfo->ssl_fd, vpninfo, buf, len);
- }
- int openconnect_dtls_write(struct openconnect_info *vpninfo, void *buf, size_t len)
- {
- return _openconnect_openssl_write(vpninfo->dtls_ssl, vpninfo->dtls_fd, vpninfo, buf, len);
- }
- /* set ms to zero for no timeout */
- static int _openconnect_openssl_read(SSL *ssl, int fd, struct openconnect_info *vpninfo, char *buf, size_t len, unsigned ms)
- {
- int done, ret;
- struct timeval timeout, *tv = NULL;
- if (ms) {
- timeout.tv_sec = ms/1000;
- timeout.tv_usec = (ms%1000)*1000;
- tv = &timeout;
- }
- while ((done = SSL_read(ssl, buf, len)) == -1) {
- int err = SSL_get_error(ssl, done);
- fd_set wr_set, rd_set;
- int maxfd = fd;
- FD_ZERO(&wr_set);
- FD_ZERO(&rd_set);
- if (err == SSL_ERROR_WANT_READ)
- FD_SET(fd, &rd_set);
- else if (err == SSL_ERROR_WANT_WRITE)
- FD_SET(fd, &wr_set);
- else {
- vpn_progress(vpninfo, PRG_ERR, _("Failed to read from TLS/DTLS socket\n"));
- openconnect_report_ssl_errors(vpninfo);
- return -EIO;
- }
- cmd_fd_set(vpninfo, &rd_set, &maxfd);
- ret = select(maxfd + 1, &rd_set, &wr_set, NULL, tv);
- if (is_cancel_pending(vpninfo, &rd_set)) {
- vpn_progress(vpninfo, PRG_ERR, _("TLS/DTLS read cancelled\n"));
- return -EINTR;
- }
- if (ret == 0) {
- return -ETIMEDOUT;
- }
- }
- return done;
- }
- static int openconnect_openssl_read(struct openconnect_info *vpninfo, char *buf, size_t len)
- {
- return _openconnect_openssl_read(vpninfo->https_ssl, vpninfo->ssl_fd, vpninfo, buf, len, 0);
- }
- int openconnect_dtls_read(struct openconnect_info *vpninfo, void *buf, size_t len, unsigned ms)
- {
- return _openconnect_openssl_read(vpninfo->dtls_ssl, vpninfo->dtls_fd, vpninfo, buf, len, ms);
- }
- static int openconnect_openssl_gets(struct openconnect_info *vpninfo, char *buf, size_t len)
- {
- int i = 0;
- int ret;
- if (len < 2)
- return -EINVAL;
- while (1) {
- ret = SSL_read(vpninfo->https_ssl, buf + i, 1);
- if (ret == 1) {
- if (buf[i] == '\n') {
- buf[i] = 0;
- if (i && buf[i-1] == '\r') {
- buf[i-1] = 0;
- i--;
- }
- return i;
- }
- i++;
- if (i >= len - 1) {
- buf[i] = 0;
- return i;
- }
- } else {
- fd_set rd_set, wr_set;
- int maxfd = vpninfo->ssl_fd;
- FD_ZERO(&rd_set);
- FD_ZERO(&wr_set);
- ret = SSL_get_error(vpninfo->https_ssl, ret);
- if (ret == SSL_ERROR_WANT_READ)
- FD_SET(vpninfo->ssl_fd, &rd_set);
- else if (ret == SSL_ERROR_WANT_WRITE)
- FD_SET(vpninfo->ssl_fd, &wr_set);
- else {
- vpn_progress(vpninfo, PRG_ERR, _("Failed to read from TLS/DTLS socket\n"));
- openconnect_report_ssl_errors(vpninfo);
- ret = -EIO;
- break;
- }
- cmd_fd_set(vpninfo, &rd_set, &maxfd);
- select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
- if (is_cancel_pending(vpninfo, &rd_set)) {
- vpn_progress(vpninfo, PRG_ERR, _("TLS/DTLS read cancelled\n"));
- ret = -EINTR;
- break;
- }
- }
- }
- buf[i] = 0;
- return i ?: ret;
- }
- int ssl_nonblock_read(struct openconnect_info *vpninfo, int dtls, void *buf, int maxlen)
- {
- SSL *ssl = dtls ? vpninfo->dtls_ssl : vpninfo->https_ssl;
- int len, ret;
- if (!ssl) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Attempted to read from non-existent %s session\n"),
- dtls ? "DTLS" : "TLS");
- return -1;
- }
- len = SSL_read(ssl, buf, maxlen);
- if (len > 0)
- return len;
- ret = SSL_get_error(ssl, len);
- if (ret == SSL_ERROR_WANT_WRITE || ret == SSL_ERROR_WANT_READ)
- return 0;
- vpn_progress(vpninfo, PRG_ERR, _("Read error on %s session: %d\n"),
- dtls ? "DTLS" : "TLS", ret);
- return -EIO;
- }
- int ssl_nonblock_write(struct openconnect_info *vpninfo, int dtls, void *buf, int buflen)
- {
- SSL *ssl = dtls ? vpninfo->dtls_ssl : vpninfo->https_ssl;
- int ret;
- if (!ssl) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Attempted to write to non-existent %s session\n"),
- dtls ? "DTLS" : "TLS");
- return -1;
- }
- ret = SSL_write(ssl, buf, buflen);
- if (ret > 0)
- return ret;
- ret = SSL_get_error(ssl, ret);
- switch (ret) {
- case SSL_ERROR_WANT_WRITE:
- /* Waiting for the socket to become writable -- it's
- probably stalled, and/or the buffers are full */
- if (dtls)
- monitor_write_fd(vpninfo, dtls);
- else
- monitor_write_fd(vpninfo, ssl);
- /* Fall through */
- case SSL_ERROR_WANT_READ:
- return 0;
- default:
- vpn_progress(vpninfo, PRG_ERR, _("Write error on %s session: %d\n"),
- dtls ? "DTLS" : "TLS", ret);
- openconnect_report_ssl_errors(vpninfo);
- return -1;
- }
- }
- /* UI handling. All this just to handle the PIN callback from the TPM ENGINE,
- and turn it into a call to our ->process_auth_form function */
- struct ui_data {
- struct openconnect_info *vpninfo;
- struct oc_form_opt **last_opt;
- struct oc_auth_form form;
- };
- struct ui_form_opt {
- struct oc_form_opt opt;
- UI_STRING *uis;
- };
- #ifdef HAVE_ENGINE
- static int ui_open(UI *ui)
- {
- struct openconnect_info *vpninfo = UI_get0_user_data(ui);
- struct ui_data *ui_data;
- if (!vpninfo || !vpninfo->process_auth_form)
- return 0;
- ui_data = malloc(sizeof(*ui_data));
- if (!ui_data)
- return 0;
- memset(ui_data, 0, sizeof(*ui_data));
- ui_data->last_opt = &ui_data->form.opts;
- ui_data->vpninfo = vpninfo;
- ui_data->form.auth_id = (char *)"openssl_ui";
- UI_add_user_data(ui, ui_data);
- return 1;
- }
- static int ui_write(UI *ui, UI_STRING *uis)
- {
- struct ui_data *ui_data = UI_get0_user_data(ui);
- struct ui_form_opt *opt;
- switch (UI_get_string_type(uis)) {
- case UIT_ERROR:
- ui_data->form.error = (char *)UI_get0_output_string(uis);
- break;
- case UIT_INFO:
- ui_data->form.message = (char *)UI_get0_output_string(uis);
- break;
- case UIT_PROMPT:
- opt = malloc(sizeof(*opt));
- if (!opt)
- return 1;
- memset(opt, 0, sizeof(*opt));
- opt->uis = uis;
- opt->opt.label = opt->opt.name = (char *)UI_get0_output_string(uis);
- if (UI_get_input_flags(uis) & UI_INPUT_FLAG_ECHO)
- opt->opt.type = OC_FORM_OPT_TEXT;
- else
- opt->opt.type = OC_FORM_OPT_PASSWORD;
- *(ui_data->last_opt) = &opt->opt;
- ui_data->last_opt = &opt->opt.next;
- break;
- default:
- vpn_progress(ui_data->vpninfo, PRG_ERR,
- _("Unhandled SSL UI request type %d\n"),
- UI_get_string_type(uis));
- return 0;
- }
- return 1;
- }
- static int ui_flush(UI *ui)
- {
- struct ui_data *ui_data = UI_get0_user_data(ui);
- struct openconnect_info *vpninfo = ui_data->vpninfo;
- struct ui_form_opt *opt;
- int ret;
- ret = process_auth_form(vpninfo, &ui_data->form);
- if (ret)
- return 0;
- for (opt = (struct ui_form_opt *)ui_data->form.opts; opt;
- opt = (struct ui_form_opt *)opt->opt.next) {
- if (opt->opt._value && opt->uis)
- UI_set_result(ui, opt->uis, opt->opt._value);
- }
- return 1;
- }
- static int ui_close(UI *ui)
- {
- struct ui_data *ui_data = UI_get0_user_data(ui);
- struct ui_form_opt *opt, *next_opt;
- opt = (struct ui_form_opt *)ui_data->form.opts;
- while (opt) {
- next_opt = (struct ui_form_opt *)opt->opt.next;
- if (opt->opt._value)
- free(opt->opt._value);
- free(opt);
- opt = next_opt;
- }
- free(ui_data);
- UI_add_user_data(ui, NULL);
- return 1;
- }
- static UI_METHOD *create_openssl_ui(void)
- {
- UI_METHOD *ui_method = UI_create_method((char *)"AnyConnect VPN UI");
- /* Set up a UI method of our own for password/passphrase requests */
- UI_method_set_opener(ui_method, ui_open);
- UI_method_set_writer(ui_method, ui_write);
- UI_method_set_flusher(ui_method, ui_flush);
- UI_method_set_closer(ui_method, ui_close);
- return ui_method;
- }
- #endif
- static int pem_pw_cb(char *buf, int len, int w, void *ci)
- {
- struct cert_info *certinfo = ci;
- struct openconnect_info *vpninfo = certinfo->vpninfo;
- char *pass = NULL;
- int plen;
- if (certinfo->password) {
- pass = certinfo->password;
- certinfo->password = NULL;
- } else if (request_passphrase(vpninfo,
- certinfo_string(certinfo, "openconnect_pem", "openconnect_secondary_pem"),
- &pass,
- certinfo_string(certinfo, _("Enter PEM pass phrase:"),
- _("Enter secondary PEM pass phrase:"))))
- return -1;
- plen = strlen(pass);
- if (len <= plen) {
- vpn_progress(vpninfo, PRG_ERR,
- _("PEM password too long (%d >= %d)\n"),
- plen, len);
- free_pass(&pass);
- return -1;
- }
- memcpy(buf, pass, plen+1);
- free_pass(&pass);
- return plen;
- }
- struct ossl_cert_info {
- EVP_PKEY *key;
- X509 *cert;
- STACK_OF(X509) *extra_certs;
- const char *certs_from;
- };
- void unload_certificate(struct cert_info *certinfo, int finalize)
- {
- (void) finalize;
- if (!certinfo)
- return;
- if (certinfo->priv_info) {
- struct ossl_cert_info *oci = certinfo->priv_info;
- certinfo->priv_info = NULL;
- if (oci->key)
- EVP_PKEY_free(oci->key);
- if (oci->cert)
- X509_free(oci->cert);
- if (oci->extra_certs)
- sk_X509_pop_free(oci->extra_certs, X509_free);
- free(oci);
- }
- }
- static int install_ssl_ctx_certs(struct openconnect_info *vpninfo, struct ossl_cert_info *oci)
- {
- X509 *cert = oci->cert;
- int i;
- if (!cert || !oci->key) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Client certificate or key missing\n"));
- return -EINVAL;
- }
- if (!SSL_CTX_use_PrivateKey(vpninfo->https_ctx, oci->key)) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Loading private key failed\n"));
- openconnect_report_ssl_errors(vpninfo);
- return -EIO;
- }
- if (!SSL_CTX_use_certificate(vpninfo->https_ctx, cert)) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to install certificate in OpenSSL context\n"));
- openconnect_report_ssl_errors(vpninfo);
- return -EIO;
- }
- vpninfo->cert_x509 = cert;
- X509_up_ref(cert);
- if (!oci->extra_certs)
- return 0;
- next:
- for (i = 0; i < sk_X509_num(oci->extra_certs); i++) {
- X509 *cert2 = sk_X509_value(oci->extra_certs, i);
- if (X509_check_issued(cert2, cert) == X509_V_OK) {
- char buf[200];
- if (cert2 == cert)
- break;
- if (X509_check_issued(cert2, cert2) == X509_V_OK)
- break;
- X509_NAME_oneline(X509_get_subject_name(cert2),
- buf, sizeof(buf));
- vpn_progress(vpninfo, PRG_DEBUG,
- _("Extra cert from %s: '%s'\n"), oci->certs_from, buf);
- X509_up_ref(cert2);
- SSL_CTX_add_extra_chain_cert(vpninfo->https_ctx, cert2);
- cert = cert2;
- goto next;
- }
- }
- return 0;
- }
- static int load_pkcs12_certificate(struct openconnect_info *vpninfo, struct cert_info *certinfo,
- struct ossl_cert_info *oci, PKCS12 *p12)
- {
- EVP_PKEY *pkey = NULL;
- X509 *cert = NULL;
- STACK_OF(X509) *ca;
- int ret = 0;
- char *pass;
- pass = certinfo->password;
- certinfo->password = NULL;
- retrypass:
- /* We do this every time round the loop, to work around a bug in
- OpenSSL < 1.0.0-beta2 -- where the stack at *ca will be freed
- when PKCS12_parse() returns an error, but *ca is left pointing
- to the freed memory. */
- ca = NULL;
- if (!PKCS12_parse(p12, pass, &pkey, &cert, &ca)) {
- unsigned long err = ERR_peek_error();
- if (ERR_GET_LIB(err) == ERR_LIB_PKCS12 &&
- #if OPENSSL_VERSION_NUMBER < 0x30000000L
- ERR_GET_FUNC(err) == PKCS12_F_PKCS12_PARSE &&
- #endif
- ERR_GET_REASON(err) == PKCS12_R_MAC_VERIFY_FAILURE) {
- if (pass)
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to decrypt PKCS#12 certificate file\n"));
- free_pass(&pass);
- if (request_passphrase(vpninfo,
- certinfo_string(certinfo, "openconnect_pkcs12",
- "openconnect_secondary_pkcs12"),
- &pass,
- certinfo_string(certinfo, _("Enter PKCS#12 pass phrase:"),
- _("Enter secondary PKCS#12 pass phrase:"))) < 0) {
- PKCS12_free(p12);
- return -EINVAL;
- }
- goto retrypass;
- }
- openconnect_report_ssl_errors(vpninfo);
- vpn_progress(vpninfo, PRG_ERR,
- certinfo_string(certinfo, _("Parse PKCS#12 failed (see above errors)\n"),
- _("Parse secondary PKCS#12 failed (see above errors)\n")));
- PKCS12_free(p12);
- free_pass(&pass);
- return -EINVAL;
- }
- free_pass(&pass);
- if (cert) {
- char buf[200];
- X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
- vpn_progress(vpninfo, PRG_INFO,
- certinfo_string(certinfo, _("Using client certificate '%s'\n"),
- _("Using secondary certificate '%s'\n")), buf);
- } else {
- vpn_progress(vpninfo, PRG_ERR,
- certinfo_string(certinfo, _("PKCS#12 contained no certificate!\n"),
- _("Secondary PKCS#12 contained no certificate!\n")));
- ret = -EINVAL;
- }
- if (!pkey) {
- vpn_progress(vpninfo, PRG_ERR,
- certinfo_string(certinfo, _("PKCS#12 contained no private key!\n"),
- _("Secondary PKCS#12 contained no private key!\n")));
- ret = -EINVAL;
- }
- oci->key = pkey;
- oci->cert = cert;
- oci->extra_certs = ca;
- oci->certs_from = _("PKCS#12");
- PKCS12_free(p12);
- if (ret)
- unload_certificate(certinfo, 1);
- return ret;
- }
- #ifdef HAVE_ENGINE
- static int load_tpm_certificate(struct openconnect_info *vpninfo, struct cert_info *certinfo,
- struct ossl_cert_info *oci, const char *engine)
- {
- ENGINE *e;
- EVP_PKEY *key;
- UI_METHOD *meth = NULL;
- int ret = 0;
- ENGINE_load_builtin_engines();
- e = ENGINE_by_id(engine);
- if (!e && !strcmp(engine, "tpm2")) {
- ERR_clear_error();
- e = ENGINE_by_id("tpm2tss");
- }
- if (!e) {
- vpn_progress(vpninfo, PRG_ERR, _("Can't load TPM engine.\n"));
- openconnect_report_ssl_errors(vpninfo);
- return -EINVAL;
- }
- if (!ENGINE_init(e) || !ENGINE_set_default_RSA(e) ||
- !ENGINE_set_default_RAND(e)) {
- vpn_progress(vpninfo, PRG_ERR, _("Failed to init TPM engine\n"));
- openconnect_report_ssl_errors(vpninfo);
- ENGINE_free(e);
- return -EINVAL;
- }
- if (certinfo->password) {
- if (!ENGINE_ctrl_cmd(e, "PIN", strlen(certinfo->password),
- certinfo->password, NULL, 0)) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to set TPM SRK password\n"));
- openconnect_report_ssl_errors(vpninfo);
- }
- free_pass(&certinfo->password);
- }
- /* Provide our own UI method to handle the PIN callback. */
- meth = create_openssl_ui();
- key = ENGINE_load_private_key(e, certinfo->key, meth, vpninfo);
- if (meth)
- UI_destroy_method(meth);
- if (!key) {
- vpn_progress(vpninfo, PRG_ERR,
- certinfo_string(certinfo, _("Failed to load TPM private key\n"),
- _("Failed to load secondary TPM private key\n")));
- openconnect_report_ssl_errors(vpninfo);
- ret = -EINVAL;
- goto out;
- }
- oci->key = key;
- out:
- ENGINE_finish(e);
- ENGINE_free(e);
- return ret;
- }
- #else
- static int load_tpm_certificate(struct openconnect_info *vpninfo, struct cert_info *certinfo,
- struct ossl_cert_info *oci, const char *engine)
- {
- vpn_progress(vpninfo, PRG_ERR,
- _("This version of OpenConnect was built without TPM support\n"));
- return -EINVAL;
- }
- #endif
- /* This is a reimplementation of SSL_CTX_use_certificate_chain_file().
- * We do this for three reasons:
- *
- * - Firstly, we have no way to obtain the primary X509 certificate
- * after SSL_CTX_use_certificate_chain_file() has loaded it, and we
- * need to inspect it to check for expiry and report its name etc.
- * So in the past we've opened the cert file again and read the cert
- * again in a reload_pem_cert() function which was a partial
- * reimplementation anyway.
- *
- * - Secondly, on Windows, OpenSSL only partially handles UTF-8 filenames.
- * Specifically, BIO_new_file() will convert UTF-8 to UTF-16 and attempt
- * to use _wfopen() to open the file, but BIO_read_filename() will not.
- * It is BIO_read_filename() which the SSL_CTX_*_file functions use, and
- * thus they don't work with UTF-8 file names. This is filed as RT#3479:
- * http://rt.openssl.org/Ticket/Display.html?id=3479
- *
- * - Finally, and least importantly, it does actually matter which supporting
- * certs we offer on the wire because of RT#1942. Doing this for ourselves
- * allows us to explicitly print the supporting certs that we're using,
- * which may assist in diagnosing problems.
- */
- static int load_cert_chain_file(struct openconnect_info *vpninfo, struct cert_info *certinfo,
- struct ossl_cert_info *oci)
- {
- BIO *b;
- FILE *f = openconnect_fopen_utf8(vpninfo, certinfo->cert, "rb");
- STACK_OF(X509) *extra_certs = NULL;
- char buf[200];
- if (!f) {
- vpn_progress(vpninfo, PRG_ERR,
- certinfo_string(certinfo, _("Failed to open certificate file %s: %s\n"),
- _("Failed to open secondary certificate file %s: %s\n")),
- certinfo->cert, strerror(errno));
- return -ENOENT;
- }
- b = BIO_new_fp(f, 1);
- if (!b) {
- fclose(f);
- err:
- vpn_progress(vpninfo, PRG_ERR,
- _("Loading certificate failed\n"));
- openconnect_report_ssl_errors(vpninfo);
- return -EIO;
- }
- oci->cert = PEM_read_bio_X509_AUX(b, NULL, NULL, NULL);
- if (!oci->cert) {
- BIO_free(b);
- goto err;
- }
- X509_NAME_oneline(X509_get_subject_name(oci->cert), buf, sizeof(buf));
- vpn_progress(vpninfo, PRG_INFO,
- certinfo_string(certinfo, _("Using client certificate '%s'\n"),
- _("Using secondary certificate '%s'\n")), buf);
- while (1) {
- X509 *x = PEM_read_bio_X509(b, NULL, NULL, NULL);
- if (!x) {
- unsigned long err = ERR_peek_last_error();
- if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
- ERR_GET_REASON(err) == PEM_R_NO_START_LINE)
- ERR_clear_error();
- else
- goto err_extra;
- break;
- }
- if (!extra_certs)
- extra_certs = sk_X509_new_null();
- if (!extra_certs) {
- err_extra:
- vpn_progress(vpninfo, PRG_ERR,
- certinfo_string(certinfo, _("Failed to process all supporting certs. Trying anyway...\n"),
- _("Failed to process secondary supporting certs. Trying anyway...\n")));
- openconnect_report_ssl_errors(vpninfo);
- X509_free(x);
- /* It might work without... */
- break;
- }
- if (!sk_X509_push(extra_certs, x))
- goto err_extra;
- }
- BIO_free(b);
- oci->extra_certs = extra_certs;
- oci->certs_from = _("PEM file");
- return 0;
- }
- #ifdef ANDROID_KEYSTORE
- static BIO *BIO_from_keystore(struct openconnect_info *vpninfo, const char *item)
- {
- unsigned char *content;
- BIO *b;
- int len;
- const char *p = item + 9;
- /* Skip first two slashes if the user has given it as
- keystore://foo ... */
- if (*p == '/')
- p++;
- if (*p == '/')
- p++;
- len = keystore_fetch(p, &content);
- if (len < 0) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to load item '%s' from keystore: %s\n"),
- p, keystore_strerror(len));
- return NULL;
- }
- if (!(b = BIO_new(BIO_s_mem())) || BIO_write(b, content, len) != len) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to create BIO for keystore item '%s'\n"),
- p);
- free(content);
- BIO_free(b);
- return NULL;
- }
- free(content);
- return b;
- }
- #endif
- static int is_pem_password_error(struct openconnect_info *vpninfo, struct cert_info *certinfo)
- {
- unsigned long err = ERR_peek_error();
- openconnect_report_ssl_errors(vpninfo);
- #ifndef EVP_F_EVP_DECRYPTFINAL_EX
- #define EVP_F_EVP_DECRYPTFINAL_EX EVP_F_EVP_DECRYPTFINAL
- #endif
- /* If the user fat-fingered the passphrase, try again */
- if (ERR_GET_LIB(err) == ERR_LIB_EVP &&
- #if OPENSSL_VERSION_NUMBER < 0x30000000L
- ERR_GET_FUNC(err) == EVP_F_EVP_DECRYPTFINAL_EX &&
- #endif
- ERR_GET_REASON(err) == EVP_R_BAD_DECRYPT) {
- vpn_progress(vpninfo, PRG_ERR,
- certinfo_string(certinfo, _("Loading private key failed (wrong passphrase?)\n"),
- _("Loading secondary private key failed (wrong passphrase?)\n")));
- ERR_clear_error();
- return 1;
- }
- vpn_progress(vpninfo, PRG_ERR,
- certinfo_string(certinfo, _("Loading private key failed (see above errors)\n"),
- _("Loading secondary private key failed (see above errors)\n")));
- return 0;
- }
- static int xload_certificate(struct openconnect_info *vpninfo, struct cert_info *certinfo,
- struct ossl_cert_info *oci)
- {
- FILE *f;
- char buf[256];
- int ret;
- if (!strncmp(certinfo->cert, "pkcs11:", 7)) {
- int ret = load_pkcs11_certificate(vpninfo, certinfo, &oci->cert);
- if (ret)
- return ret;
- goto got_cert;
- }
- vpn_progress(vpninfo, PRG_DEBUG,
- certinfo_string(certinfo, _("Using certificate file %s\n"),
- _("Using secondary certificate file %s\n")),
- certinfo->cert);
- if (strncmp(certinfo->cert, "keystore:", 9)) {
- PKCS12 *p12;
- f = openconnect_fopen_utf8(vpninfo, certinfo->cert, "rb");
- if (!f) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to open certificate file %s: %s\n"),
- certinfo->cert, strerror(errno));
- return -ENOENT;
- }
- p12 = d2i_PKCS12_fp(f, NULL);
- fclose(f);
- if (p12)
- return load_pkcs12_certificate(vpninfo, certinfo, oci, p12);
- /* Not PKCS#12. Clear error and fall through to see if it's a PEM file... */
- ERR_clear_error();
- }
- /* It's PEM or TPM now, and either way we need to load the plain cert: */
- #ifdef ANDROID_KEYSTORE
- if (!strncmp(certinfo->cert, "keystore:", 9)) {
- BIO *b = BIO_from_keystore(vpninfo, certinfo->cert);
- if (!b)
- return -EINVAL;
- oci->cert = PEM_read_bio_X509_AUX(b, NULL, pem_pw_cb, certinfo);
- BIO_free(b);
- if (!oci->cert) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to load X509 certificate from keystore\n"));
- openconnect_report_ssl_errors(vpninfo);
- return -EINVAL;
- }
- } else
- #endif /* ANDROID_KEYSTORE */
- {
- int ret = load_cert_chain_file(vpninfo, certinfo, oci);
- if (ret)
- return ret;
- }
- got_cert:
- #ifdef ANDROID_KEYSTORE
- if (!strncmp(certinfo->key, "keystore:", 9)) {
- BIO *b;
- again_android:
- b = BIO_from_keystore(vpninfo, certinfo->key);
- if (!b)
- return -EINVAL;
- oci->key = PEM_read_bio_PrivateKey(b, NULL, pem_pw_cb, certinfo);
- BIO_free(b);
- if (!oci->key) {
- if (is_pem_password_error(vpninfo, certinfo))
- goto again_android;
- return -EINVAL;
- }
- return 0;
- }
- #endif /* ANDROID_KEYSTORE */
- if (!strncmp(certinfo->key, "pkcs11:", 7))
- return load_pkcs11_key(vpninfo, certinfo, &oci->key);
- f = openconnect_fopen_utf8(vpninfo, certinfo->key, "rb");
- if (!f) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to open private key file %s: %s\n"),
- certinfo->key, strerror(errno));
- return -ENOENT;
- }
- buf[255] = 0;
- while (fgets(buf, 255, f)) {
- if (!strcmp(buf, "-----BEGIN TSS KEY BLOB-----\n")) {
- fclose(f);
- return load_tpm_certificate(vpninfo, certinfo, oci, "tpm");
- } else if (!strcmp(buf, "-----BEGIN TSS2 KEY BLOB-----\n") ||
- !strcmp(buf, "-----BEGIN TSS2 PRIVATE KEY-----\n")) {
- fclose(f);
- return load_tpm_certificate(vpninfo, certinfo, oci, "tpm2");
- } else if (!strcmp(buf, "-----BEGIN RSA PRIVATE KEY-----\n") ||
- !strcmp(buf, "-----BEGIN DSA PRIVATE KEY-----\n") ||
- !strcmp(buf, "-----BEGIN EC PRIVATE KEY-----\n") ||
- !strcmp(buf, "-----BEGIN ENCRYPTED PRIVATE KEY-----\n") ||
- !strcmp(buf, "-----BEGIN PRIVATE KEY-----\n")) {
- BIO *b = BIO_new_fp(f, BIO_CLOSE);
- if (!b) {
- fclose(f);
- vpn_progress(vpninfo, PRG_ERR,
- certinfo_string(certinfo, _("Loading private key failed\n"),
- _("Loading secondary private key failed\n")));
- openconnect_report_ssl_errors(vpninfo);
- return -EINVAL;
- }
- again:
- fseek(f, 0, SEEK_SET);
- oci->key = PEM_read_bio_PrivateKey(b, NULL, pem_pw_cb, certinfo);
- if (!oci->key) {
- if (is_pem_password_error(vpninfo, certinfo))
- goto again;
- BIO_free(b);
- return -EINVAL;
- }
- ret = 0;
- BIO_free(b);
- return ret;
- }
- }
- /* Not PEM? Try DER... */
- fseek(f, 0, SEEK_SET);
- /* This will catch PKCS#1 and unencrypted PKCS#8
- * (except in OpenSSL 0.9.8 where it doesn't handle
- * the latter but nobody cares about 0.9.8 any more. */
- oci->key = d2i_PrivateKey_fp(f, NULL);
- if (oci->key) {
- ret = 0;
- fclose(f);
- return ret;
- } else {
- /* Encrypted PKCS#8 DER */
- X509_SIG *p8;
- fseek(f, 0, SEEK_SET);
- p8 = d2i_PKCS8_fp(f, NULL);
- if (p8) {
- PKCS8_PRIV_KEY_INFO *p8inf;
- char *pass = certinfo->password;
- fclose(f);
- while (!(p8inf = PKCS8_decrypt(p8, pass ? : "", pass ? strlen(pass) : 0))) {
- unsigned long err = ERR_peek_error();
- if (ERR_GET_LIB(err) == ERR_LIB_EVP &&
- #if OPENSSL_VERSION_NUMBER < 0x30000000L
- ERR_GET_FUNC(err) == EVP_F_EVP_DECRYPTFINAL_EX &&
- #endif
- ERR_GET_REASON(err) == EVP_R_BAD_DECRYPT) {
- ERR_clear_error();
- if (pass) {
- vpn_progress(vpninfo, PRG_ERR,
- certinfo_string(certinfo, _("Failed to decrypt PKCS#8 certificate file\n"),
- _("Failed to decrypt secondary PKCS#8 certificate file\n")));
- free_pass(&pass);
- pass = NULL;
- }
- if (request_passphrase(vpninfo,
- certinfo_string(certinfo, "openconnect_pkcs8",
- "openconnect_secondary_pkcs8"),
- &pass,
- certinfo_string(certinfo, _("Enter PKCS#8 pass phrase:"),
- _("Enter PKCS#8 secondary pass phrase:"))) >= 0)
- continue;
- } else {
- vpn_progress(vpninfo, PRG_ERR,
- certinfo_string(certinfo, _("Failed to decrypt PKCS#8 certificate file\n"),
- _("Failed to decrypt secondary PKCS#8 certificate file\n")));
- openconnect_report_ssl_errors(vpninfo);
- }
- free_pass(&pass);
- certinfo->password = NULL;
- X509_SIG_free(p8);
- return -EINVAL;
- }
- free_pass(&pass);
- certinfo->password = NULL;
- oci->key = EVP_PKCS82PKEY(p8inf);
- PKCS8_PRIV_KEY_INFO_free(p8inf);
- X509_SIG_free(p8);
- if (oci->key == NULL) {
- vpn_progress(vpninfo, PRG_ERR,
- certinfo_string(certinfo, _("Failed to convert PKCS#8 to OpenSSL EVP_PKEY\n"),
- _("Failed to convert secondary PKCS#8 to OpenSSL EVP_PKEY\n")));
- return -EIO;
- }
- ret = 0;
- return ret;
- }
- }
- fclose(f);
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to identify private key type in '%s'\n"),
- certinfo->key);
- return -EINVAL;
- }
- int load_certificate(struct openconnect_info *vpninfo, struct cert_info *certinfo, int flags)
- {
- struct ossl_cert_info *oci;
- int ret;
- (void) flags;
- certinfo->priv_info = oci = calloc(1, sizeof(*oci));
- if (!oci) {
- ret = -ENOMEM;
- goto done;
- }
- certinfo->vpninfo = vpninfo;
- ret = xload_certificate(vpninfo, certinfo, oci);
- done:
- if (ret)
- unload_certificate(certinfo, 1);
- return ret;
- }
- static int get_cert_fingerprint(struct openconnect_info *vpninfo,
- X509 *cert, const EVP_MD *type,
- char *buf)
- {
- unsigned char md[EVP_MAX_MD_SIZE];
- unsigned int i, n;
- if (!X509_digest(cert, type, md, &n))
- return -ENOMEM;
- for (i = 0; i < n; i++)
- sprintf(&buf[i*2], "%02X", md[i]);
- return 0;
- }
- int get_cert_md5_fingerprint(struct openconnect_info *vpninfo,
- void *cert, char *buf)
- {
- return get_cert_fingerprint(vpninfo, cert, EVP_md5(), buf);
- }
- static int set_peer_cert_hash(struct openconnect_info *vpninfo)
- {
- EVP_PKEY *pkey;
- BIO *bp = BIO_new(BIO_s_mem());
- BUF_MEM *keyinfo;
- /* We can't use X509_pubkey_digest() because it only hashes the
- subjectPublicKey BIT STRING, and not the whole of the
- SubjectPublicKeyInfo SEQUENCE. */
- pkey = X509_get_pubkey(vpninfo->peer_cert);
- if (!i2d_PUBKEY_bio(bp, pkey)) {
- EVP_PKEY_free(pkey);
- BIO_free(bp);
- return -ENOMEM;
- }
- EVP_PKEY_free(pkey);
- BIO_get_mem_ptr(bp, &keyinfo);
- openconnect_sha256(vpninfo->peer_cert_sha256_raw, keyinfo->data, keyinfo->length);
- openconnect_sha1(vpninfo->peer_cert_sha1_raw, keyinfo->data, keyinfo->length);
- BIO_free(bp);
- return 0;
- }
- #if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
- static int match_hostname_elem(const char *hostname, int helem_len,
- const char *match, int melem_len)
- {
- if (!helem_len && !melem_len)
- return 0;
- if (!helem_len || !melem_len)
- return -1;
- if (match[0] == '*') {
- int i;
- for (i = 1 ; i <= helem_len; i++) {
- if (!match_hostname_elem(hostname + i, helem_len - i,
- match + 1, melem_len - 1))
- return 0;
- }
- return -1;
- }
- /* From the NetBSD (5.1) man page for ctype(3):
- Values of type char or signed char must first be cast to unsigned char,
- to ensure that the values are within the correct range. The result
- should then be cast to int to avoid warnings from some compilers.
- We do indeed get warning "array subscript has type 'char'" without
- the casts. Ick. */
- if (toupper((int)(unsigned char)hostname[0]) ==
- toupper((int)(unsigned char)match[0]))
- return match_hostname_elem(hostname + 1, helem_len - 1,
- match + 1, melem_len - 1);
- return -1;
- }
- static int match_hostname(const char *hostname, const char *match)
- {
- while (*match) {
- const char *h_dot, *m_dot;
- int helem_len, melem_len;
- h_dot = strchr(hostname, '.');
- m_dot = strchr(match, '.');
- if (h_dot && m_dot) {
- helem_len = h_dot - hostname + 1;
- melem_len = m_dot - match + 1;
- } else if (!h_dot && !m_dot) {
- helem_len = strlen(hostname);
- melem_len = strlen(match);
- } else
- return -1;
- if (match_hostname_elem(hostname, helem_len,
- match, melem_len))
- return -1;
- hostname += helem_len;
- match += melem_len;
- }
- if (*hostname)
- return -1;
- return 0;
- }
- /* cf. RFC2818 and RFC2459 */
- static int match_cert_hostname(struct openconnect_info *vpninfo, X509 *peer_cert,
- const unsigned char *ipaddr, int ipaddrlen)
- {
- STACK_OF(GENERAL_NAME) *altnames;
- X509_NAME *subjname;
- ASN1_STRING *subjasn1;
- char *subjstr = NULL;
- int i, altdns = 0;
- int ret;
- altnames = X509_get_ext_d2i(peer_cert, NID_subject_alt_name,
- NULL, NULL);
- for (i = 0; i < sk_GENERAL_NAME_num(altnames); i++) {
- const GENERAL_NAME *this = sk_GENERAL_NAME_value(altnames, i);
- if (this->type == GEN_DNS) {
- char *str;
- int len = ASN1_STRING_to_UTF8((void *)&str, this->d.ia5);
- if (len < 0)
- continue;
- altdns = 1;
- /* We don't like names with embedded NUL */
- if (strlen(str) != len)
- continue;
- if (!match_hostname(vpninfo->hostname, str)) {
- vpn_progress(vpninfo, PRG_DEBUG,
- _("Matched DNS altname '%s'\n"),
- str);
- GENERAL_NAMES_free(altnames);
- OPENSSL_free(str);
- return 0;
- } else {
- vpn_progress(vpninfo, PRG_DEBUG,
- _("No match for altname '%s'\n"),
- str);
- }
- OPENSSL_free(str);
- } else if (this->type == GEN_IPADD && ipaddrlen) {
- char host[80];
- int family;
- if (this->d.ip->length == 4) {
- family = AF_INET;
- } else if (this->d.ip->length == 16) {
- family = AF_INET6;
- } else {
- vpn_progress(vpninfo, PRG_ERR,
- _("Certificate has GEN_IPADD altname with bogus length %d\n"),
- this->d.ip->length);
- continue;
- }
- /* We only do this for the debug messages */
- inet_ntop(family, this->d.ip->data, host, sizeof(host));
- if (this->d.ip->length == ipaddrlen &&
- !memcmp(ipaddr, this->d.ip->data, ipaddrlen)) {
- vpn_progress(vpninfo, PRG_DEBUG,
- _("Matched %s address '%s'\n"),
- (family == AF_INET6) ? "IPv6" : "IPv4",
- host);
- GENERAL_NAMES_free(altnames);
- return 0;
- } else {
- vpn_progress(vpninfo, PRG_DEBUG,
- _("No match for %s address '%s'\n"),
- (family == AF_INET6) ? "IPv6" : "IPv4",
- host);
- }
- } else if (this->type == GEN_URI) {
- char *str;
- char *url_proto, *url_host, *url_path, *url_host2;
- int url_port;
- int len = ASN1_STRING_to_UTF8((void *)&str, this->d.ia5);
- if (len < 0)
- continue;
- /* We don't like names with embedded NUL */
- if (strlen(str) != len)
- continue;
- if (internal_parse_url(str, &url_proto, &url_host, &url_port, &url_path, 0)) {
- OPENSSL_free(str);
- continue;
- }
- if (!url_proto || strcasecmp(url_proto, "https"))
- goto no_uri_match;
- if (url_port != vpninfo->port)
- goto no_uri_match;
- /* Leave url_host as it was so that it can be freed */
- url_host2 = url_host;
- if (ipaddrlen == 16 && vpninfo->hostname[0] != '[' &&
- url_host[0] == '[' && url_host[strlen(url_host)-1] == ']') {
- /* Cope with https://[IPv6]/ when the hostname is bare IPv6 */
- url_host[strlen(url_host)-1] = 0;
- url_host2++;
- }
- if (strcasecmp(vpninfo->hostname, url_host2))
- goto no_uri_match;
- if (url_path) {
- vpn_progress(vpninfo, PRG_DEBUG,
- _("URI '%s' has non-empty path; ignoring\n"),
- str);
- goto no_uri_match_silent;
- }
- vpn_progress(vpninfo, PRG_DEBUG,
- _("Matched URI '%s'\n"),
- str);
- free(url_proto);
- free(url_host);
- free(url_path);
- OPENSSL_free(str);
- GENERAL_NAMES_free(altnames);
- return 0;
- no_uri_match:
- vpn_progress(vpninfo, PRG_DEBUG,
- _("No match for URI '%s'\n"),
- str);
- no_uri_match_silent:
- free(url_proto);
- free(url_host);
- free(url_path);
- OPENSSL_free(str);
- }
- }
- GENERAL_NAMES_free(altnames);
- /* According to RFC2818, we don't use the legacy subject name if
- there was an altname with DNS type. */
- if (altdns) {
- vpn_progress(vpninfo, PRG_ERR,
- _("No altname in peer cert matched '%s'\n"),
- vpninfo->hostname);
- return -EINVAL;
- }
- subjname = X509_get_subject_name(peer_cert);
- if (!subjname) {
- vpn_progress(vpninfo, PRG_ERR,
- _("No subject name in peer cert!\n"));
- return -EINVAL;
- }
- /* Find the _last_ (most specific) commonName */
- i = -1;
- while (1) {
- int j = X509_NAME_get_index_by_NID(subjname, NID_commonName, i);
- if (j >= 0)
- i = j;
- else
- break;
- }
- subjasn1 = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subjname, i));
- i = ASN1_STRING_to_UTF8((void *)&subjstr, subjasn1);
- if (!subjstr || strlen(subjstr) != i) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to parse subject name in peer cert\n"));
- return -EINVAL;
- }
- ret = 0;
- if (match_hostname(vpninfo->hostname, subjstr)) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Peer cert subject mismatch ('%s' != '%s')\n"),
- subjstr, vpninfo->hostname);
- ret = -EINVAL;
- } else {
- vpn_progress(vpninfo, PRG_DEBUG,
- _("Matched peer certificate subject name '%s'\n"),
- subjstr);
- }
- OPENSSL_free(subjstr);
- return ret;
- }
- #else
- static int match_cert_hostname(struct openconnect_info *vpninfo, X509 *peer_cert,
- const unsigned char *ipaddr, int ipaddrlen)
- {
- char *matched = NULL;
- if (ipaddrlen && X509_check_ip(peer_cert, ipaddr, ipaddrlen, 0) == 1) {
- if (vpninfo->verbose >= PRG_DEBUG) {
- char host[80];
- int family;
- if (ipaddrlen == 4)
- family = AF_INET;
- else
- family = AF_INET6;
- /* In Windows, the 'src' argument of inet_ntop() isn't const */
- inet_ntop(family, (void *)ipaddr, host, sizeof(host));
- vpn_progress(vpninfo, PRG_DEBUG,
- _("Matched %s address '%s'\n"),
- (family == AF_INET6) ? "IPv6" : "IPv4",
- host);
- }
- return 0;
- }
- if (X509_check_host(peer_cert, vpninfo->hostname, 0, 0, &matched) == 1) {
- vpn_progress(vpninfo, PRG_DEBUG,
- _("Matched peer certificate subject name '%s'\n"),
- matched);
- OPENSSL_free(matched);
- return 0;
- }
- /* We do it like this because these two strings are already
- * translated in gnutls.c */
- vpn_progress(vpninfo, PRG_INFO,
- _("Server certificate verify failed: %s\n"),
- _("certificate does not match hostname"));
- return -EINVAL;
- }
- #endif /* OpenSSL < 1.0.2 */
- /* Before OpenSSL 1.1 we could do this directly. And needed to. */
- #ifndef SSL_CTX_get_extra_chain_certs_only
- #define SSL_CTX_get_extra_chain_certs_only(ctx, st) \
- (void)(*(st) = (ctx)->extra_certs)
- #endif
- static void workaround_openssl_certchain_bug(struct openconnect_info *vpninfo,
- SSL *ssl)
- {
- /* OpenSSL has problems with certificate chains -- if there are
- multiple certs with the same name, it doesn't necessarily
- choose the _right_ one. (RT#1942)
- Pick the right ones for ourselves and add them manually. */
- X509 *cert = SSL_get_certificate(ssl);
- X509 *cert2;
- X509_STORE *store = SSL_CTX_get_cert_store(vpninfo->https_ctx);
- X509_STORE_CTX *ctx;
- void *extra_certs;
- X509_STORE_CTX_get_issuer_fn issuer_fn;
- if (!cert || !store)
- return;
- /* If we already have 'supporting' certs, don't add them again */
- SSL_CTX_get_extra_chain_certs_only(vpninfo->https_ctx, &extra_certs);
- if (extra_certs)
- return;
- ctx = X509_STORE_CTX_new();
- if (!ctx)
- return;
- if (X509_STORE_CTX_init(ctx, store, NULL, NULL))
- goto out;
- issuer_fn = X509_STORE_CTX_get_get_issuer(ctx);
- while (issuer_fn(&cert2, ctx, cert) == 1) {
- char buf[200];
- if (cert2 == cert)
- break;
- if (X509_check_issued(cert2, cert2) == X509_V_OK)
- break;
- cert = cert2;
- X509_NAME_oneline(X509_get_subject_name(cert),
- buf, sizeof(buf));
- vpn_progress(vpninfo, PRG_DEBUG,
- _("Extra cert from cafile: '%s'\n"), buf);
- SSL_CTX_add_extra_chain_cert(vpninfo->https_ctx, cert);
- }
- out:
- X509_STORE_CTX_free(ctx);
- }
- int openconnect_get_peer_cert_chain(struct openconnect_info *vpninfo,
- struct oc_cert **chainp)
- {
- struct oc_cert *chain, *p;
- X509_STORE_CTX *ctx = vpninfo->cert_list_handle;
- STACK_OF(X509) *untrusted;
- int i, cert_list_size;
- if (!ctx)
- return -EINVAL;
- untrusted = X509_STORE_CTX_get0_untrusted(ctx);
- if (!untrusted)
- return -EINVAL;
- cert_list_size = sk_X509_num(untrusted);
- if (!cert_list_size)
- return -EIO;
- p = chain = calloc(cert_list_size, sizeof(struct oc_cert));
- if (!chain)
- return -ENOMEM;
- for (i = 0; i < cert_list_size; i++, p++) {
- X509 *cert = sk_X509_value(untrusted, i);
- p->der_len = i2d_X509(cert, &p->der_data);
- if (p->der_len < 0) {
- openconnect_free_peer_cert_chain(vpninfo, chain);
- return -ENOMEM;
- }
- }
- *chainp = chain;
- return cert_list_size;
- }
- void openconnect_free_peer_cert_chain(struct openconnect_info *vpninfo,
- struct oc_cert *chain)
- {
- int i;
- for (i = 0; i < vpninfo->cert_list_size; i++)
- OPENSSL_free(chain[i].der_data);
- free(chain);
- }
- static int ssl_app_verify_callback(X509_STORE_CTX *ctx, void *arg)
- {
- struct openconnect_info *vpninfo = arg;
- const char *err_string = NULL;
- X509 *cert = X509_STORE_CTX_get0_cert(ctx);
- #ifdef X509_V_FLAG_PARTIAL_CHAIN
- X509_VERIFY_PARAM *param;
- #endif
- if (vpninfo->peer_cert) {
- /* This is a *rehandshake*. Require that the server
- * presents exactly the same certificate as the
- * first time. */
- if (X509_cmp(cert, vpninfo->peer_cert)) {
- vpn_progress(vpninfo, PRG_ERR, _("Server presented different cert on rehandshake\n"));
- return 0;
- }
- vpn_progress(vpninfo, PRG_TRACE, _("Server presented identical cert on rehandshake\n"));
- return 1;
- }
- vpninfo->peer_cert = cert;
- X509_up_ref(cert);
- set_peer_cert_hash(vpninfo);
- #ifdef X509_V_FLAG_PARTIAL_CHAIN
- param = X509_STORE_CTX_get0_param(ctx);
- if (param)
- X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_PARTIAL_CHAIN);
- #endif
- if (!X509_verify_cert(ctx)) {
- err_string = X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx));
- } else {
- unsigned char addrbuf[sizeof(struct in6_addr)];
- int addrlen = 0;
- if (inet_pton(AF_INET, vpninfo->hostname, addrbuf) > 0)
- addrlen = 4;
- else if (inet_pton(AF_INET6, vpninfo->hostname, addrbuf) > 0)
- addrlen = 16;
- else if (vpninfo->hostname[0] == '[' &&
- vpninfo->hostname[strlen(vpninfo->hostname)-1] == ']') {
- char *p = &vpninfo->hostname[strlen(vpninfo->hostname)-1];
- *p = 0;
- if (inet_pton(AF_INET6, vpninfo->hostname + 1, addrbuf) > 0)
- addrlen = 16;
- *p = ']';
- }
- if (match_cert_hostname(vpninfo, vpninfo->peer_cert, addrbuf, addrlen))
- err_string = _("certificate does not match hostname");
- else
- return 1;
- }
- vpn_progress(vpninfo, PRG_INFO,
- _("Server certificate verify failed: %s\n"),
- err_string);
- if (vpninfo->validate_peer_cert) {
- int ret;
- vpninfo->cert_list_handle = ctx;
- ret = vpninfo->validate_peer_cert(vpninfo->cbdata, err_string);
- vpninfo->cert_list_handle = NULL;
- if (!ret)
- return 1;
- }
- return 0;
- }
- static int check_certificate_expiry(struct openconnect_info *vpninfo, struct cert_info *certinfo,
- struct ossl_cert_info *oci)
- {
- method_const ASN1_TIME *notAfter;
- const char *reason = NULL;
- time_t t;
- int i;
- if (!oci->cert)
- return 0;
- t = time(NULL);
- notAfter = X509_get0_notAfter(oci->cert);
- i = X509_cmp_time(notAfter, &t);
- if (!i) {
- vpn_progress(vpninfo, PRG_ERR,
- certinfo_string(certinfo, _("Error in client cert notAfter field\n"),
- _("Error in secondary client cert notAfter field\n")));
- return -EINVAL;
- } else if (i < 0) {
- reason = certinfo_string(certinfo, _("Client certificate has expired at"),
- _("Secondary client certificate has expired at"));
- } else {
- t += vpninfo->cert_expire_warning;
- i = X509_cmp_time(notAfter, &t);
- if (i < 0)
- reason = certinfo_string(certinfo, _("Client certificate expires soon at"),
- _("Secondary client certificate expires soon at"));
- }
- if (reason) {
- BIO *bp = BIO_new(BIO_s_mem());
- BUF_MEM *bm;
- const char *expiry = _("<error>");
- char zero = 0;
- if (bp) {
- ASN1_TIME_print(bp, notAfter);
- BIO_write(bp, &zero, 1);
- BIO_get_mem_ptr(bp, &bm);
- expiry = bm->data;
- }
- vpn_progress(vpninfo, PRG_ERR, "%s: %s\n", reason, expiry);
- if (bp)
- BIO_free(bp);
- }
- return 0;
- }
- static int load_primary_certificate(struct openconnect_info *vpninfo)
- {
- struct cert_info *certinfo = &vpninfo->certinfo[0];
- struct ossl_cert_info *oci;
- int ret = load_certificate(vpninfo, certinfo, 0);
- oci = certinfo->priv_info;
- if (!ret)
- ret = install_ssl_ctx_certs(vpninfo, oci);
- if (!ret && !SSL_CTX_check_private_key(vpninfo->https_ctx)) {
- vpn_progress(vpninfo, PRG_ERR,
- _("SSL certificate and key do not match\n"));
- ret = -EINVAL;
- }
- if (!ret)
- check_certificate_expiry(vpninfo, &vpninfo->certinfo[0], oci);
- unload_certificate(certinfo, 1);
- return ret;
- }
- int openconnect_install_ctx_verify(struct openconnect_info *vpninfo, SSL_CTX *ctx)
- {
- /* We've seen certificates in the wild which don't have the
- purpose fields filled in correctly */
- SSL_CTX_set_purpose(ctx, X509_PURPOSE_ANY);
- SSL_CTX_set_cert_verify_callback(ctx, ssl_app_verify_callback,
- vpninfo);
- if (!vpninfo->no_system_trust)
- SSL_CTX_set_default_verify_paths(ctx);
- #ifdef ANDROID_KEYSTORE
- if (vpninfo->cafile && !strncmp(vpninfo->cafile, "keystore:", 9)) {
- STACK_OF(X509_INFO) *stack;
- X509_STORE *store;
- X509_INFO *info;
- BIO *b = BIO_from_keystore(vpninfo, vpninfo->cafile);
- if (!b)
- return -EINVAL;
- stack = PEM_X509_INFO_read_bio(b, NULL, NULL, NULL);
- BIO_free(b);
- if (!stack) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to read certs from CA file '%s'\n"),
- vpninfo->cafile);
- openconnect_report_ssl_errors(vpninfo);
- return -ENOENT;
- }
- store = SSL_CTX_get_cert_store(ctx);
- while ((info = sk_X509_INFO_pop(stack))) {
- if (info->x509)
- X509_STORE_add_cert(store, info->x509);
- if (info->crl)
- X509_STORE_add_crl(store, info->crl);
- X509_INFO_free(info);
- }
- sk_X509_INFO_free(stack);
- } else
- #endif
- if (vpninfo->cafile) {
- /* OpenSSL does actually manage to cope with UTF-8 for
- this one, under Windows. So only convert for legacy
- UNIX. */
- char *cafile = openconnect_utf8_to_legacy(vpninfo,
- vpninfo->cafile);
- int err = SSL_CTX_load_verify_locations(ctx, cafile, NULL);
- if (cafile != vpninfo->cafile)
- free(cafile);
- if (!err) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to open CA file '%s'\n"),
- vpninfo->cafile);
- openconnect_report_ssl_errors(vpninfo);
- return -EINVAL;
- }
- }
- return 0;
- }
- int openconnect_open_https(struct openconnect_info *vpninfo)
- {
- SSL *https_ssl;
- BIO *https_bio;
- int ssl_sock;
- int err;
- if (vpninfo->https_ssl)
- return 0;
- if (vpninfo->peer_cert) {
- X509_free(vpninfo->peer_cert);
- vpninfo->peer_cert = NULL;
- }
- free(vpninfo->peer_cert_hash);
- vpninfo->peer_cert_hash = NULL;
- free(vpninfo->cstp_cipher);
- vpninfo->cstp_cipher = NULL;
- ssl_sock = connect_https_socket(vpninfo);
- if (ssl_sock < 0)
- return ssl_sock;
- if (!vpninfo->https_ctx) {
- #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
- vpninfo->https_ctx = SSL_CTX_new(SSLv23_client_method());
- if (vpninfo->https_ctx)
- SSL_CTX_set_options(vpninfo->https_ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3);
- #else
- vpninfo->https_ctx = SSL_CTX_new(TLS_client_method());
- if (vpninfo->https_ctx &&
- !SSL_CTX_set_min_proto_version(vpninfo->https_ctx, TLS1_VERSION)) {
- SSL_CTX_free(vpninfo->https_ctx);
- vpninfo->https_ctx = NULL;
- }
- #endif
- if (!vpninfo->https_ctx) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Create TLSv1 CTX failed\n"));
- openconnect_report_ssl_errors(vpninfo);
- return -EINVAL;
- }
- /* Try to work around the broken firewalls which reject ClientHello
- * packets in certain size ranges. If we have SSL_OP_TLSEXT_PADDING
- * use it, else fall back to SSL_OP_NO_TICKET which mostly worked for
- * a long time. */
- #if defined(SSL_OP_TLSEXT_PADDING)
- SSL_CTX_set_options(vpninfo->https_ctx, SSL_OP_TLSEXT_PADDING);
- #elif defined(SSL_OP_NO_TICKET)
- SSL_CTX_set_options(vpninfo->https_ctx, SSL_OP_NO_TICKET);
- #endif
- #if OPENSSL_VERSION_NUMBER >= 0x010100000L
- if (vpninfo->allow_insecure_crypto) {
- /* OpenSSL versions after 1.1.0 added the notion of a "security level"
- * that enforces checks on certificates and ciphers.
- * These security levels overlap in functionality with the ciphersuite
- * priority/allow-strings.
- *
- * For now we will set the security level to 0, thus reverting
- * to the functionality seen in versions before 1.1.0. */
- SSL_CTX_set_security_level(vpninfo->https_ctx, 0);
- /* OpenSSL 3.0.0 refuses legacy renegotiation by default.
- * Current versions of the Cisco ASA doesn't seem to cope */
- SSL_CTX_set_options(vpninfo->https_ctx, SSL_OP_LEGACY_SERVER_CONNECT);
- }
- #endif
- if (vpninfo->certinfo[0].cert) {
- err = load_primary_certificate(vpninfo);
- if (err) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Loading certificate failed. Aborting.\n"));
- SSL_CTX_free(vpninfo->https_ctx);
- vpninfo->https_ctx = NULL;
- closesocket(ssl_sock);
- return err;
- }
- }
- if (!vpninfo->ciphersuite_config) {
- struct oc_text_buf *buf = buf_alloc();
- if (vpninfo->pfs)
- buf_append(buf, "HIGH:!aNULL:!eNULL:-RSA");
- else if (vpninfo->allow_insecure_crypto)
- buf_append(buf, "ALL");
- else
- buf_append(buf, "DEFAULT:-3DES:-RC4");
- if (buf_error(buf)) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to construct OpenSSL cipher list\n"));
- return buf_free(buf);
- }
- vpninfo->ciphersuite_config = buf->data;
- buf->data = NULL;
- buf_free(buf);
- }
- if (!SSL_CTX_set_cipher_list(vpninfo->https_ctx, vpninfo->ciphersuite_config)) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to set OpenSSL cipher list (\"%s\")\n"),
- vpninfo->ciphersuite_config);
- openconnect_report_ssl_errors(vpninfo);
- SSL_CTX_free(vpninfo->https_ctx);
- vpninfo->https_ctx = NULL;
- closesocket(ssl_sock);
- return -EIO;
- }
- err = openconnect_install_ctx_verify(vpninfo, vpninfo->https_ctx);
- if (err) {
- SSL_CTX_free(vpninfo->https_ctx);
- vpninfo->https_ctx = NULL;
- closesocket(ssl_sock);
- return err;
- }
- }
- https_ssl = SSL_new(vpninfo->https_ctx);
- workaround_openssl_certchain_bug(vpninfo, https_ssl);
- https_bio = BIO_new_socket(ssl_sock, BIO_NOCLOSE);
- BIO_set_nbio(https_bio, 1);
- SSL_set_bio(https_ssl, https_bio, https_bio);
- /*
- * If a ClientHello is between 256 and 511 bytes, the
- * server cannot distinguish between a SSLv2 formatted
- * packet and a SSLv3 formatted packet.
- *
- * F5 BIG-IP reverse proxies in particular will
- * silently drop an ambiguous ClientHello.
- *
- * OpenSSL fixes this in v1.0.1g+ by padding ClientHello
- * packets to at least 512 bytes.
- *
- * For older versions of OpenSSL, we try to avoid long
- * packets by silently disabling extensions such as SNI.
- *
- * Discussion:
- * https://www.ietf.org/mail-archive/web/tls/current/msg10423.html
- *
- * OpenSSL commits:
- * 4fcdd66fff5fea0cfa1055c6680a76a4303f28a2
- * cd6bd5ffda616822b52104fee0c4c7d623fd4f53
- */
- #if OPENSSL_VERSION_NUMBER >= 0x10001070 && !defined(LIBRESSL_VERSION_NUMBER)
- if (string_is_hostname(vpninfo->hostname))
- SSL_set_tlsext_host_name(https_ssl, vpninfo->hostname);
- #endif
- SSL_set_verify(https_ssl, SSL_VERIFY_PEER, NULL);
- vpn_progress(vpninfo, PRG_INFO, _("SSL negotiation with %s\n"),
- vpninfo->hostname);
- while ((err = SSL_connect(https_ssl)) <= 0) {
- fd_set wr_set, rd_set;
- int maxfd = ssl_sock;
- FD_ZERO(&wr_set);
- FD_ZERO(&rd_set);
- err = SSL_get_error(https_ssl, err);
- if (err == SSL_ERROR_WANT_READ)
- FD_SET(ssl_sock, &rd_set);
- else if (err == SSL_ERROR_WANT_WRITE)
- FD_SET(ssl_sock, &wr_set);
- else {
- vpn_progress(vpninfo, PRG_ERR, _("SSL connection failure\n"));
- openconnect_report_ssl_errors(vpninfo);
- SSL_free(https_ssl);
- closesocket(ssl_sock);
- return -EINVAL;
- }
- cmd_fd_set(vpninfo, &rd_set, &maxfd);
- select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
- if (is_cancel_pending(vpninfo, &rd_set)) {
- vpn_progress(vpninfo, PRG_ERR, _("SSL connection cancelled\n"));
- SSL_free(https_ssl);
- closesocket(ssl_sock);
- return -EINVAL;
- }
- }
- if (asprintf(&vpninfo->cstp_cipher, "%s-%s",
- SSL_get_version(https_ssl), SSL_get_cipher_name(https_ssl)) < 0) {
- SSL_free(https_ssl);
- closesocket(ssl_sock);
- return -ENOMEM;
- }
- vpninfo->ssl_fd = ssl_sock;
- vpninfo->https_ssl = https_ssl;
- vpninfo->ssl_read = openconnect_openssl_read;
- vpninfo->ssl_write = openconnect_openssl_write;
- vpninfo->ssl_gets = openconnect_openssl_gets;
- vpn_progress(vpninfo, PRG_INFO, _("Connected to HTTPS on %s with ciphersuite %s\n"),
- vpninfo->hostname, vpninfo->cstp_cipher);
- return 0;
- }
- int cstp_handshake(struct openconnect_info *vpninfo, unsigned init)
- {
- return -EOPNOTSUPP;
- }
- void openconnect_close_https(struct openconnect_info *vpninfo, int final)
- {
- if (vpninfo->https_ssl) {
- SSL_free(vpninfo->https_ssl);
- vpninfo->https_ssl = NULL;
- }
- if (vpninfo->ssl_fd != -1) {
- unmonitor_fd(vpninfo, ssl);
- closesocket(vpninfo->ssl_fd);
- vpninfo->ssl_fd = -1;
- }
- if (final) {
- if (vpninfo->https_ctx) {
- SSL_CTX_free(vpninfo->https_ctx);
- vpninfo->https_ctx = NULL;
- }
- if (vpninfo->cert_x509) {
- X509_free(vpninfo->cert_x509);
- vpninfo->cert_x509 = NULL;
- }
- }
- }
- int openconnect_init_ssl(void)
- {
- #ifdef _WIN32
- int ret = openconnect__win32_sock_init();
- if (ret)
- return ret;
- #endif
- #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
- SSL_library_init();
- ERR_clear_error();
- SSL_load_error_strings();
- OpenSSL_add_all_algorithms();
- #endif
- return 0;
- }
- char *openconnect_get_peer_cert_details(struct openconnect_info *vpninfo)
- {
- BIO *bp = BIO_new(BIO_s_mem());
- BUF_MEM *certinfo;
- char zero = 0;
- char *ret;
- X509_print_ex(bp, vpninfo->peer_cert, 0, 0);
- BIO_write(bp, &zero, 1);
- BIO_get_mem_ptr(bp, &certinfo);
- ret = strdup(certinfo->data);
- BIO_free(bp);
- return ret;
- }
- void openconnect_free_cert_info(struct openconnect_info *vpninfo,
- void *buf)
- {
- free(buf);
- }
- int openconnect_local_cert_md5(struct openconnect_info *vpninfo,
- char *buf)
- {
- buf[0] = 0;
- if (!vpninfo->cert_x509)
- return -EIO;
- if (get_cert_md5_fingerprint(vpninfo, vpninfo->cert_x509, buf))
- return -EIO;
- return 0;
- }
- #ifdef HAVE_LIBPCSCLITE
- int openconnect_hash_yubikey_password(struct openconnect_info *vpninfo,
- const char *password, int pwlen,
- const void *ident, int id_len)
- {
- if (!PKCS5_PBKDF2_HMAC_SHA1(password, pwlen, ident, id_len, 1000, 16,
- vpninfo->yubikey_pwhash))
- return -EIO;
- return 0;
- }
- int openconnect_yubikey_chalresp(struct openconnect_info *vpninfo,
- const void *challenge, int chall_len, void *result)
- {
- unsigned int mdlen = SHA1_SIZE;
- if (!HMAC(EVP_sha1(), vpninfo->yubikey_pwhash, 16, challenge, chall_len, result, &mdlen))
- return -EIO;
- return 0;
- }
- #endif
- int hotp_hmac(struct openconnect_info *vpninfo, const void *challenge)
- {
- unsigned char hash[64]; /* Enough for a SHA256 */
- unsigned int hashlen = sizeof(hash);
- const EVP_MD *alg;
- switch(vpninfo->oath_hmac_alg) {
- case OATH_ALG_HMAC_SHA1:
- alg = EVP_sha1();
- break;
- case OATH_ALG_HMAC_SHA256:
- alg = EVP_sha256();
- break;
- case OATH_ALG_HMAC_SHA512:
- alg = EVP_sha512();
- break;
- default:
- vpn_progress(vpninfo, PRG_ERR,
- _("Unsupported OATH HMAC algorithm\n"));
- return -EINVAL;
- }
- if (!HMAC(alg, vpninfo->oath_secret, vpninfo->oath_secret_len,
- challenge, 8, hash, &hashlen)) {
- vpninfo->progress(vpninfo, PRG_ERR,
- _("Failed to calculate OATH HMAC\n"));
- openconnect_report_ssl_errors(vpninfo);
- return -EINVAL;
- }
- hashlen = hash[hashlen - 1] & 15;
- return load_be32(&hash[hashlen]) & 0x7fffffff;
- }
- static long ttls_ctrl_func(BIO *b, int cmd, long larg, void *iarg);
- static int ttls_pull_func(BIO *b, char *buf, int len);
- static int ttls_push_func(BIO *b, const char *buf, int len);
- #ifdef HAVE_BIO_METH_FREE
- static BIO_METHOD *eap_ttls_method(void)
- {
- BIO_METHOD *meth = BIO_meth_new(BIO_get_new_index(), "EAP-TTLS");
- BIO_meth_set_write(meth, ttls_push_func);
- BIO_meth_set_read(meth, ttls_pull_func);
- BIO_meth_set_ctrl(meth, ttls_ctrl_func);
- return meth;
- }
- #else /* !HAVE_BIO_METH_FREE */
- #define BIO_TYPE_EAP_TTLS 0x80
- static BIO_METHOD ttls_bio_meth = {
- .type = BIO_TYPE_EAP_TTLS,
- .name = "EAP-TTLS",
- .bwrite = ttls_push_func,
- .bread = ttls_pull_func,
- .ctrl = ttls_ctrl_func,
- };
- static BIO_METHOD *eap_ttls_method(void)
- {
- return &ttls_bio_meth;
- }
- static inline void BIO_set_data(BIO *b, void *p)
- {
- b->ptr = p;
- }
- static inline void *BIO_get_data(BIO *b)
- {
- return b->ptr;
- }
- static void BIO_set_init(BIO *b, int i)
- {
- b->init = i;
- }
- #endif /* !HAVE_BIO_METH_FREE */
- static int ttls_push_func(BIO *b, const char *buf, int len)
- {
- struct openconnect_info *vpninfo = BIO_get_data(b);
- int ret = pulse_eap_ttls_send(vpninfo, buf, len);
- if (ret >= 0)
- return ret;
- return 0;
- }
- static int ttls_pull_func(BIO *b, char *buf, int len)
- {
- struct openconnect_info *vpninfo = BIO_get_data(b);
- int ret = pulse_eap_ttls_recv(vpninfo, buf, len);
- if (ret >= 0)
- return ret;
- return 0;
- }
- static long ttls_ctrl_func(BIO *b, int cmd, long larg, void *iarg)
- {
- switch(cmd) {
- case BIO_CTRL_FLUSH:
- return 1;
- default:
- return 0;
- }
- }
- void *establish_eap_ttls(struct openconnect_info *vpninfo)
- {
- SSL *ttls_ssl = NULL;
- BIO *bio;
- int err;
- if (!vpninfo->ttls_bio_meth)
- vpninfo->ttls_bio_meth = eap_ttls_method();
- bio = BIO_new(vpninfo->ttls_bio_meth);
- BIO_set_data(bio, vpninfo);
- BIO_set_init(bio, 1);
- ttls_ssl = SSL_new(vpninfo->https_ctx);
- workaround_openssl_certchain_bug(vpninfo, ttls_ssl);
- SSL_set_bio(ttls_ssl, bio, bio);
- SSL_set_verify(ttls_ssl, SSL_VERIFY_PEER, NULL);
- vpn_progress(vpninfo, PRG_INFO, _("EAP-TTLS negotiation with %s\n"),
- vpninfo->hostname);
- err = SSL_connect(ttls_ssl);
- if (err == 1) {
- vpn_progress(vpninfo, PRG_TRACE,
- _("Established EAP-TTLS session\n"));
- return ttls_ssl;
- }
- err = SSL_get_error(ttls_ssl, err);
- vpn_progress(vpninfo, PRG_ERR, _("EAP-TTLS connection failure %d\n"), err);
- openconnect_report_ssl_errors(vpninfo);
- SSL_free(ttls_ssl);
- return NULL;
- }
- void destroy_eap_ttls(struct openconnect_info *vpninfo, void *ttls)
- {
- SSL_free(ttls);
- /* Leave the BIO_METH for now. It may get reused and we don't want to
- * have to call BIO_get_new_index() more times than is necessary */
- }
- #ifdef HAVE_HPKE_SUPPORT
- static int generate_strap_key(EC_KEY **key, char **pubkey,
- unsigned char *privder_in, int privderlen,
- unsigned char **pubder, int *pubderlen)
- {
- EC_KEY *lkey;
- struct oc_text_buf *buf = NULL;
- unsigned char *der = NULL;
- int len;
- if (privder_in) {
- lkey = d2i_ECPrivateKey(NULL, (const unsigned char **)&privder_in, privderlen);
- if (!lkey)
- return -EIO;
- } else {
- lkey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
- if (!lkey)
- return -EIO;
- if (!EC_KEY_generate_key(lkey)) {
- EC_KEY_free(lkey);
- return -EIO;
- }
- }
- len = i2d_EC_PUBKEY(lkey, &der);
- buf = buf_alloc();
- buf_append_base64(buf, der, len, 0);
- if (buf_error(buf)) {
- EC_KEY_free(lkey);
- free(der);
- return buf_free(buf);
- }
- /* All done. There are no failure modes from here on, so
- * install the resulting key/pubkey/etc. where the caller
- * asked us to, freeing the previous ones if needed. */
- EC_KEY_free(*key);
- *key = lkey;
- free(*pubkey);
- *pubkey = buf->data;
- /* If the caller wants the DER, give it to them */
- if (pubder && pubderlen) {
- *pubder = der;
- *pubderlen = len;
- } else {
- free(der);
- }
- buf->data = NULL;
- buf_free(buf);
- return 0;
- }
- int generate_strap_keys(struct openconnect_info *vpninfo)
- {
- int err;
- err = generate_strap_key(&vpninfo->strap_key, &vpninfo->strap_pubkey,
- NULL, 0, NULL, NULL);
- if (err) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to generate STRAP key"));
- openconnect_report_ssl_errors(vpninfo);
- free_strap_keys(vpninfo);
- return -EIO;
- }
- err = generate_strap_key(&vpninfo->strap_dh_key, &vpninfo->strap_dh_pubkey,
- NULL, 0, NULL, NULL);
- if (err) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to generate STRAP DH key\n"));
- openconnect_report_ssl_errors(vpninfo);
- free_strap_keys(vpninfo);
- return -EIO;
- }
- return 0;
- }
- void free_strap_keys(struct openconnect_info *vpninfo)
- {
- if (vpninfo->strap_key)
- EC_KEY_free(vpninfo->strap_key);
- if (vpninfo->strap_dh_key)
- EC_KEY_free(vpninfo->strap_dh_key);
- vpninfo->strap_key = vpninfo->strap_dh_key = NULL;
- }
- int ingest_strap_privkey(struct openconnect_info *vpninfo, unsigned char *der, int len)
- {
- if (generate_strap_key(&vpninfo->strap_key,
- &vpninfo->strap_pubkey, der, len, NULL, 0)) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to decode STRAP key\n"));
- openconnect_report_ssl_errors(vpninfo);
- return -EIO;
- }
- return 0;
- }
- void append_strap_privkey(struct openconnect_info *vpninfo,
- struct oc_text_buf *buf)
- {
- unsigned char *der = NULL;
- int derlen = i2d_ECPrivateKey(vpninfo->strap_key, &der);
- if (derlen > 0)
- buf_append_base64(buf, der, derlen, 0);
- }
- #include <openssl/kdf.h>
- int ecdh_compute_secp256r1(struct openconnect_info *vpninfo, const unsigned char *pubkey,
- int pubkey_len, unsigned char *secret)
- {
- const EC_POINT *point;
- EC_KEY *pkey;
- int ret = 0;
- if (!(pkey = d2i_EC_PUBKEY(NULL, &pubkey, pubkey_len)) ||
- !(point = EC_KEY_get0_public_key(pkey))) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to decode server DH key\n"));
- openconnect_report_ssl_errors(vpninfo);
- ret = -EIO;
- goto out;
- }
- /* Perform the DH secret derivation from our STRAP-DH key
- * and the one the server returned to us in the payload. */
- if (ECDH_compute_key(secret, 32, point, vpninfo->strap_dh_key, NULL) <= 0) {
- vpn_progress(vpninfo, PRG_ERR, _("Failed to compute DH secret\n"));
- openconnect_report_ssl_errors(vpninfo);
- ret = -EIO;
- }
- out:
- EC_KEY_free(pkey);
- return ret;
- }
- int hkdf_sha256_extract_expand(struct openconnect_info *vpninfo, unsigned char *buf,
- const unsigned char *info, int infolen)
- {
- size_t buflen = 32;
- int ret = 0;
- /* Next, use HKDF to generate the actual key used for encryption. */
- EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
- if (!ctx || !EVP_PKEY_derive_init(ctx) ||
- !EVP_PKEY_CTX_set_hkdf_md(ctx, EVP_sha256()) ||
- !EVP_PKEY_CTX_set1_hkdf_key(ctx, buf, buflen) ||
- !EVP_PKEY_CTX_hkdf_mode(ctx, EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND) ||
- !EVP_PKEY_CTX_add1_hkdf_info(ctx, info, infolen) ||
- EVP_PKEY_derive(ctx, buf, &buflen) != 1) {
- vpn_progress(vpninfo, PRG_ERR, _("HKDF key derivation failed\n"));
- openconnect_report_ssl_errors(vpninfo);
- ret = -EINVAL;
- }
- EVP_PKEY_CTX_free(ctx);
- return ret;
- }
- int aes_256_gcm_decrypt(struct openconnect_info *vpninfo, unsigned char *key,
- unsigned char *data, int len,
- unsigned char *iv, unsigned char *tag)
- {
- /* Finally, we actually decrypt the sso-token */
- EVP_CIPHER_CTX *cctx = EVP_CIPHER_CTX_new();
- int ret = 0, i = 0;
- if (!cctx ||
- !EVP_DecryptInit_ex(cctx, EVP_aes_256_gcm(), NULL, key, iv) ||
- !EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_AEAD_SET_TAG, 12, tag) ||
- !EVP_DecryptUpdate(cctx, data, &len, data, len) ||
- !EVP_DecryptFinal(cctx, NULL, &i)) {
- vpn_progress(vpninfo, PRG_ERR, _("SSO token decryption failed\n"));
- openconnect_report_ssl_errors(vpninfo);
- ret = -EINVAL;
- }
- EVP_CIPHER_CTX_free(cctx);
- return ret;
- }
- void append_strap_verify(struct openconnect_info *vpninfo,
- struct oc_text_buf *buf, int rekey)
- {
- unsigned char finished[64];
- size_t flen = SSL_get_finished(vpninfo->https_ssl, finished, sizeof(finished));
- if (flen > sizeof(finished)) {
- vpn_progress(vpninfo, PRG_ERR,
- _("SSL Finished message too large (%zd bytes)\n"), flen);
- if (!buf_error(buf))
- buf->error = -EIO;
- return;
- }
- /* If we're rekeying, we need to sign the Verify header with the *old* key. */
- EVP_PKEY *evpkey = EVP_PKEY_new();
- if (!evpkey || EVP_PKEY_set1_EC_KEY(evpkey, vpninfo->strap_key) <= 0) {
- vpn_progress(vpninfo, PRG_ERR,
- _("STRAP signature failed\n"));
- fail_errors:
- openconnect_report_ssl_errors(vpninfo);
- fail_pkey:
- if (!buf_error(buf))
- buf->error = -EIO;
- EVP_PKEY_free(evpkey);
- return;
- }
- unsigned char *pubkey_der = NULL;
- int pubkey_derlen = 0;
- if (rekey) {
- if (generate_strap_key(&vpninfo->strap_key, &vpninfo->strap_pubkey,
- NULL, 0, &pubkey_der, &pubkey_derlen)) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to regenerate STRAP key\n"));
- goto fail_errors;
- }
- } else {
- pubkey_der = openconnect_base64_decode(&pubkey_derlen, vpninfo->strap_pubkey);
- if (!pubkey_der) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to generate STRAP key DER\n"));
- goto fail_pkey;
- }
- }
- EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
- const EVP_MD *md = EVP_sha256(); /* We only support prime256v1 for now */
- unsigned char signature_bin[128];
- size_t siglen = sizeof(signature_bin);
- int ok = (mdctx &&
- EVP_DigestSignInit(mdctx, NULL, md, NULL, evpkey) > 0 &&
- EVP_DigestSignUpdate(mdctx, finished, flen) > 0 &&
- EVP_DigestSignUpdate(mdctx, pubkey_der, pubkey_derlen) > 0 &&
- EVP_DigestSignFinal(mdctx, (void *)signature_bin, &siglen) > 0);
- EVP_MD_CTX_free(mdctx);
- EVP_PKEY_free(evpkey);
- free(pubkey_der);
- if (!ok) {
- vpn_progress(vpninfo, PRG_ERR,
- _("STRAP signature failed\n"));
- goto fail_errors;
- }
- buf_append_base64(buf, signature_bin, siglen, 0);
- }
- #endif /* HAVE_HPKE_SUPPORT */
- int export_certificate_pkcs7(struct openconnect_info *vpninfo,
- struct cert_info *certinfo,
- cert_format_t format,
- struct oc_text_buf **pp7b)
- {
- struct ossl_cert_info *oci;
- PKCS7 *p7 = NULL;
- BIO *bio = NULL;
- BUF_MEM *bptr = NULL;
- struct oc_text_buf *p7b = NULL;
- int ret, ok;
- if (!(certinfo && (oci = certinfo->priv_info) && pp7b))
- return -EINVAL;
- /* We have the client certificate in 'oci.cert' and *optionally*
- * a stack of intermediate certs in oci.extra_certs. For the TLS
- * connection those would be used by SSL_CTX_use_certificate() and
- * SSL_CTX_add_extra_chain_cert() respectively. For PKCS7_sign()
- * we need the actual cert at the head of the stack, so *create*
- * one if needed, and insert oci.cert at position zero. */
- if (!oci->extra_certs)
- oci->extra_certs = sk_X509_new_null();
- if (!oci->extra_certs)
- goto err;
- if (!sk_X509_insert(oci->extra_certs, oci->cert, 0))
- goto err;
- X509_up_ref(oci->cert);
- p7 = PKCS7_sign(NULL, NULL, oci->extra_certs, NULL, PKCS7_DETACHED);
- if (!p7) {
- err:
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to create PKCS#7 structure\n"));
- ret = -EIO;
- goto out;
- }
- ret = 0;
- bio = BIO_new(BIO_s_mem());
- if (!bio) {
- ret = -ENOMEM;
- goto pkcs7_error;
- }
- if (format == CERT_FORMAT_ASN1) {
- ok = i2d_PKCS7_bio(bio, p7);
- } else if (format == CERT_FORMAT_PEM) {
- ok = PEM_write_bio_PKCS7(bio, p7);
- } else {
- ret = -EINVAL;
- goto pkcs7_error;
- }
- if (!ok) {
- ret = -EIO;
- goto pkcs7_error;
- }
- BIO_get_mem_ptr(bio, &bptr);
- p7b = buf_alloc();
- if (!p7b)
- ret = -ENOMEM;
- if (ret < 0) {
- pkcs7_error:
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to output PKCS#7 structure\n"));
- goto out;
- }
- BIO_set_close(bio, BIO_NOCLOSE);
- p7b->data = bptr->data;
- p7b->pos = bptr->length;
- *pp7b = p7b;
- p7b = NULL;
- out:
- buf_free(p7b);
- BIO_free(bio);
- if (p7)
- PKCS7_free(p7);
- return ret;
- }
- int multicert_sign_data(struct openconnect_info *vpninfo,
- struct cert_info *certinfo,
- unsigned int hashes,
- const void *chdata, size_t chdata_len,
- struct oc_text_buf **psignature)
- {
- struct table_entry {
- openconnect_hash_type id;
- const EVP_MD *(*evp_md_fn)(void);
- };
- static struct table_entry table[] = {
- { OPENCONNECT_HASH_SHA512, &EVP_sha512 },
- { OPENCONNECT_HASH_SHA384, &EVP_sha384 },
- { OPENCONNECT_HASH_SHA256, &EVP_sha256 },
- { OPENCONNECT_HASH_UNKNOWN },
- };
- struct ossl_cert_info *oci;
- struct oc_text_buf *signature;
- openconnect_hash_type hash;
- int ret;
- /**
- * Check preconditions...
- */
- if (!(certinfo && (oci = certinfo->priv_info)
- && hashes && chdata && chdata_len && psignature))
- return -EINVAL;
- *psignature = NULL;
- signature = buf_alloc();
- if (!signature)
- goto out_of_memory;
- for (const struct table_entry *entry = table;
- (hash = entry->id) != OPENCONNECT_HASH_UNKNOWN;
- entry++) {
- if ((hashes & MULTICERT_HASH_FLAG(hash)) == 0)
- continue;
- EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
- if (!mdctx)
- goto out_of_memory;
- const EVP_MD *md = (*entry->evp_md_fn)();
- size_t siglen = 0;
- int ok = (EVP_DigestSignInit(mdctx, NULL, md, NULL, oci->key) > 0 &&
- EVP_DigestSignUpdate(mdctx, chdata, chdata_len) > 0 &&
- EVP_DigestSignFinal(mdctx, NULL, &siglen) > 0 &&
- !buf_ensure_space(signature, siglen) &&
- EVP_DigestSignFinal(mdctx, (void *)signature->data, &siglen) > 0);
- EVP_MD_CTX_free(mdctx);
- if (ok) {
- signature->pos = siglen;
- *psignature = signature;
- return hash;
- }
- }
- /** Error path */
- ret = -EIO;
- if (buf_error(signature)) {
- out_of_memory:
- ret = -ENOMEM;
- }
- buf_free(signature);
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to generate signature for multiple certificate authentication\n"));
- openconnect_report_ssl_errors(vpninfo);
- return ret;
- }
|