1 /***************************************************************************
2  begin       : Wed Apr 28 2010
3  copyright   : (C) 2010, 2016 by Martin Preuss
4  email       : martin@libchipcard.de
5 
6  ***************************************************************************
7  *                                                                         *
8  *   This library is free software; you can redistribute it and/or         *
9  *   modify it under the terms of the GNU Lesser General Public            *
10  *   License as published by the Free Software Foundation; either          *
11  *   version 2.1 of the License, or (at your option) any later version.    *
12  *                                                                         *
13  *   This library is distributed in the hope that it will be useful,       *
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
16  *   Lesser General Public License for more details.                       *
17  *                                                                         *
18  *   You should have received a copy of the GNU Lesser General Public      *
19  *   License along with this library; if not, write to the Free Software   *
20  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,                 *
21  *   MA  02111-1307  USA                                                   *
22  *                                                                         *
23  ***************************************************************************/
24 
25 #ifdef HAVE_CONFIG_H
26 # include <config.h>
27 #endif
28 
29 #define DISABLE_DEBUGLOG
30 
31 /*#define GWEN_TLS_DEBUG*/
32 
33 /* #define GWEN_TLS_USE_OLD_CODE */
34 
35 #include "syncio_tls_p.h"
36 #include "i18n_l.h"
37 
38 #include <gwenhywfar/misc.h>
39 #include <gwenhywfar/debug.h>
40 #include <gwenhywfar/gui.h>
41 #include <gwenhywfar/gui.h>
42 #include <gwenhywfar/pathmanager.h>
43 #include <gwenhywfar/directory.h>
44 #include <gwenhywfar/gwenhywfar.h>
45 #include <gwenhywfar/text.h>
46 
47 #include <assert.h>
48 #include <errno.h>
49 #include <string.h>
50 #include <stdlib.h>
51 
52 #include <gnutls/gnutls.h>
53 #include <gnutls/x509.h>
54 #include <gcrypt.h>
55 
56 
57 
58 GWEN_INHERIT(GWEN_SYNCIO, GWEN_SYNCIO_TLS)
59 
60 
61 #ifndef OS_WIN32
62 const char *SYNCIO_TLS_SYSTEM_CERTFILES[]= {
63   "/etc/ssl/certs/ca-certificates.crt",
64   "/etc/ssl/ca-bundle.pem",
65   NULL
66 };
67 #endif
68 
69 
70 
71 
GWEN_SyncIo_Tls_new(GWEN_SYNCIO * baseIo)72 GWEN_SYNCIO *GWEN_SyncIo_Tls_new(GWEN_SYNCIO *baseIo)
73 {
74   GWEN_SYNCIO *sio;
75   GWEN_SYNCIO_TLS *xio;
76 
77   assert(baseIo);
78   sio=GWEN_SyncIo_new(GWEN_SYNCIO_TLS_TYPE, baseIo);
79   GWEN_NEW_OBJECT(GWEN_SYNCIO_TLS, xio);
80   GWEN_INHERIT_SETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio, xio, GWEN_SyncIo_Tls_FreeData);
81 
82   /* preset data */
83   xio->checkCertFn=GWEN_SyncIo_Tls_Internal_CheckCert;
84 
85   /* set virtual functions */
86   GWEN_SyncIo_SetConnectFn(sio, GWEN_SyncIo_Tls_Connect);
87   GWEN_SyncIo_SetDisconnectFn(sio, GWEN_SyncIo_Tls_Disconnect);
88   GWEN_SyncIo_SetReadFn(sio, GWEN_SyncIo_Tls_Read);
89   GWEN_SyncIo_SetWriteFn(sio, GWEN_SyncIo_Tls_Write);
90 
91   return sio;
92 }
93 
94 
95 
GWEN_SyncIo_Tls_FreeData(GWEN_UNUSED void * bp,void * p)96 void GWENHYWFAR_CB GWEN_SyncIo_Tls_FreeData(GWEN_UNUSED void *bp, void *p)
97 {
98   GWEN_SYNCIO_TLS *xio;
99 
100   xio=(GWEN_SYNCIO_TLS *) p;
101   free(xio->localCertFile);
102   free(xio->localKeyFile);
103   free(xio->localTrustFile);
104   free(xio->dhParamFile);
105   free(xio->hostName);
106   GWEN_SslCertDescr_free(xio->peerCertDescr);
107   GWEN_FREE_OBJECT(xio);
108 }
109 
110 
111 
GWEN_SyncIo_Tls_SetCheckCertFn(GWEN_SYNCIO * sio,GWEN_SIO_TLS_CHECKCERT_FN f)112 GWEN_SIO_TLS_CHECKCERT_FN GWEN_SyncIo_Tls_SetCheckCertFn(GWEN_SYNCIO *sio, GWEN_SIO_TLS_CHECKCERT_FN f)
113 {
114   GWEN_SYNCIO_TLS *xio;
115   GWEN_SIO_TLS_CHECKCERT_FN oldF;
116 
117   assert(sio);
118   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
119   assert(xio);
120 
121   oldF=xio->checkCertFn;
122   xio->checkCertFn=f;
123   return oldF;
124 }
125 
126 
127 
GWEN_SyncIo_Tls_Internal_CheckCert(GWEN_SYNCIO * sio,const GWEN_SSLCERTDESCR * cert)128 GWENHYWFAR_CB int GWEN_SyncIo_Tls_Internal_CheckCert(GWEN_SYNCIO *sio,
129                                                      const GWEN_SSLCERTDESCR *cert)
130 {
131   GWEN_SYNCIO_TLS *xio;
132 
133   assert(sio);
134   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
135   assert(xio);
136 
137   DBG_WARN(GWEN_LOGDOMAIN, "No checkCertFn set, using GWEN_GUI");
138   return GWEN_Gui_CheckCert(cert, sio, 0);
139 }
140 
141 
142 
GWEN_SyncIo_Tls_CheckCert(GWEN_SYNCIO * sio,const GWEN_SSLCERTDESCR * cert)143 int GWEN_SyncIo_Tls_CheckCert(GWEN_SYNCIO *sio, const GWEN_SSLCERTDESCR *cert)
144 {
145   GWEN_SYNCIO_TLS *xio;
146 
147   assert(sio);
148   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
149   assert(xio);
150 
151   if (xio->checkCertFn) {
152     /* try my own checkCert function first */
153     return xio->checkCertFn(sio, cert);
154   }
155   else {
156     /* none set, call the check cert function of GWEN_GUI (for older code) */
157     DBG_ERROR(GWEN_LOGDOMAIN, "No checkCertFn set, falling back to GUI (SNH!)");
158     return GWEN_SyncIo_Tls_Internal_CheckCert(sio, cert);
159   }
160 }
161 
162 
163 
GWEN_SyncIo_Tls_GetLocalCertFile(const GWEN_SYNCIO * sio)164 const char *GWEN_SyncIo_Tls_GetLocalCertFile(const GWEN_SYNCIO *sio)
165 {
166   GWEN_SYNCIO_TLS *xio;
167 
168   assert(sio);
169   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
170   assert(xio);
171 
172   return xio->localCertFile;
173 }
174 
175 
176 
GWEN_SyncIo_Tls_SetLocalCertFile(GWEN_SYNCIO * sio,const char * s)177 void GWEN_SyncIo_Tls_SetLocalCertFile(GWEN_SYNCIO *sio, const char *s)
178 {
179   GWEN_SYNCIO_TLS *xio;
180 
181   assert(sio);
182   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
183   assert(xio);
184 
185   free(xio->localCertFile);
186   if (s)
187     xio->localCertFile=strdup(s);
188   else
189     xio->localCertFile=NULL;
190 }
191 
192 
193 
GWEN_SyncIo_Tls_GetLocalKeyFile(const GWEN_SYNCIO * sio)194 const char *GWEN_SyncIo_Tls_GetLocalKeyFile(const GWEN_SYNCIO *sio)
195 {
196   GWEN_SYNCIO_TLS *xio;
197 
198   assert(sio);
199   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
200   assert(xio);
201 
202   return xio->localKeyFile;
203 }
204 
205 
206 
GWEN_SyncIo_Tls_SetLocalKeyFile(GWEN_SYNCIO * sio,const char * s)207 void GWEN_SyncIo_Tls_SetLocalKeyFile(GWEN_SYNCIO *sio, const char *s)
208 {
209   GWEN_SYNCIO_TLS *xio;
210 
211   assert(sio);
212   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
213   assert(xio);
214 
215   free(xio->localKeyFile);
216   if (s)
217     xio->localKeyFile=strdup(s);
218   else
219     xio->localKeyFile=NULL;
220 }
221 
222 
223 
GWEN_SyncIo_Tls_GetLocalTrustFile(const GWEN_SYNCIO * sio)224 const char *GWEN_SyncIo_Tls_GetLocalTrustFile(const GWEN_SYNCIO *sio)
225 {
226   GWEN_SYNCIO_TLS *xio;
227 
228   assert(sio);
229   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
230   assert(xio);
231 
232   return xio->localTrustFile;
233 }
234 
235 
236 
GWEN_SyncIo_Tls_SetLocalTrustFile(GWEN_SYNCIO * sio,const char * s)237 void GWEN_SyncIo_Tls_SetLocalTrustFile(GWEN_SYNCIO *sio, const char *s)
238 {
239   GWEN_SYNCIO_TLS *xio;
240 
241   assert(sio);
242   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
243   assert(xio);
244 
245   free(xio->localTrustFile);
246   if (s)
247     xio->localTrustFile=strdup(s);
248   else
249     xio->localTrustFile=NULL;
250 }
251 
252 
253 
GWEN_SyncIo_Tls_GetDhParamFile(const GWEN_SYNCIO * sio)254 const char *GWEN_SyncIo_Tls_GetDhParamFile(const GWEN_SYNCIO *sio)
255 {
256   GWEN_SYNCIO_TLS *xio;
257 
258   assert(sio);
259   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
260   assert(xio);
261 
262   return xio->dhParamFile;
263 }
264 
265 
266 
GWEN_SyncIo_Tls_SetDhParamFile(GWEN_SYNCIO * sio,const char * s)267 void GWEN_SyncIo_Tls_SetDhParamFile(GWEN_SYNCIO *sio, const char *s)
268 {
269   GWEN_SYNCIO_TLS *xio;
270 
271   assert(sio);
272   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
273   assert(xio);
274 
275   free(xio->dhParamFile);
276   if (s)
277     xio->dhParamFile=strdup(s);
278   else
279     xio->dhParamFile=NULL;
280 }
281 
282 
283 
GWEN_SyncIo_Tls_GetRemoteHostName(const GWEN_SYNCIO * sio)284 const char *GWEN_SyncIo_Tls_GetRemoteHostName(const GWEN_SYNCIO *sio)
285 {
286   GWEN_SYNCIO_TLS *xio;
287 
288   assert(sio);
289   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
290   assert(xio);
291 
292   return xio->hostName;
293 }
294 
295 
296 
GWEN_SyncIo_Tls_SetRemoteHostName(GWEN_SYNCIO * sio,const char * s)297 void GWEN_SyncIo_Tls_SetRemoteHostName(GWEN_SYNCIO *sio, const char *s)
298 {
299   GWEN_SYNCIO_TLS *xio;
300 
301   assert(sio);
302   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
303   assert(xio);
304 
305   free(xio->hostName);
306   if (s)
307     xio->hostName=strdup(s);
308   else
309     xio->hostName=NULL;
310 }
311 
312 
313 
GWEN_SyncIo_Tls_GetPeerCertDescr(const GWEN_SYNCIO * sio)314 GWEN_SSLCERTDESCR *GWEN_SyncIo_Tls_GetPeerCertDescr(const GWEN_SYNCIO *sio)
315 {
316   GWEN_SYNCIO_TLS *xio;
317 
318   assert(sio);
319   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
320   assert(xio);
321 
322   return xio->peerCertDescr;
323 }
324 
325 
326 
GWEN_SyncIo_Tls__readFile(const char * fname,GWEN_BUFFER * buf)327 int GWEN_SyncIo_Tls__readFile(const char *fname, GWEN_BUFFER *buf)
328 {
329   FILE *f;
330 
331   f=fopen(fname, "r");
332   if (f==NULL)
333     return GWEN_ERROR_IO;
334 
335   while (!feof(f)) {
336     int rv;
337 
338     GWEN_Buffer_AllocRoom(buf, 512);
339     rv=fread(GWEN_Buffer_GetPosPointer(buf), 1, 512, f);
340     if (rv==0)
341       break;
342     else if (rv<0) {
343       DBG_INFO(GWEN_LOGDOMAIN, "fread(%s): %s", fname, strerror(errno));
344       fclose(f);
345       return GWEN_ERROR_IO;
346     }
347     else {
348       GWEN_Buffer_IncrementPos(buf, rv);
349       GWEN_Buffer_AdjustUsedBytes(buf);
350     }
351   }
352   fclose(f);
353   return 0;
354 }
355 
356 
357 
358 
359 #if GWEN_TLS_USE_SYSTEM_CERTIFICATES
360 # ifndef OS_WIN32
GWEN_SyncIo_Tls_AddCaCertFolder(GWEN_SYNCIO * sio,const char * folder)361 static int GWEN_SyncIo_Tls_AddCaCertFolder(GWEN_SYNCIO *sio, const char *folder)
362 {
363   GWEN_SYNCIO_TLS *xio;
364   int rv;
365   int successfullTustFileCount=0;
366 
367   assert(sio);
368   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
369   assert(xio);
370 
371   if (folder && *folder) {
372     GWEN_STRINGLIST *fileList;
373 
374     fileList=GWEN_StringList_new();
375     rv=GWEN_Directory_GetMatchingFilesRecursively(folder, fileList, "*.crt");
376     if (rv<0) {
377       DBG_ERROR(GWEN_LOGDOMAIN,
378                 "Error reading list of certificate files (%d) in folder [%s]",
379                 rv, folder);
380     }
381     else {
382       GWEN_STRINGLISTENTRY *se;
383 
384       se=GWEN_StringList_FirstEntry(fileList);
385       while (se) {
386         const char *s;
387 
388         s=GWEN_StringListEntry_Data(se);
389         if (s && *s) {
390           rv=gnutls_certificate_set_x509_trust_file(xio->credentials,
391                                                     s,
392                                                     GNUTLS_X509_FMT_PEM);
393           if (rv<=0) {
394             DBG_WARN(GWEN_LOGDOMAIN,
395                      "gnutls_certificate_set_x509_trust_file(%s): %d (%s)",
396                      s, rv, gnutls_strerror(rv));
397           }
398           else {
399             DBG_INFO(GWEN_LOGDOMAIN, "Added %d trusted certs from [%s]", rv, s);
400             successfullTustFileCount++;
401           }
402         }
403 
404         se=GWEN_StringListEntry_Next(se);
405       } /* while */
406     }
407     GWEN_StringList_free(fileList);
408   }
409 
410   if (successfullTustFileCount==0) {
411     DBG_ERROR(GWEN_LOGDOMAIN, "No files added from folder [%s]", folder);
412   }
413 
414   return successfullTustFileCount;
415 }
416 # endif
417 #endif
418 
419 
420 
GWEN_SyncIo_Tls_Prepare(GWEN_SYNCIO * sio)421 int GWEN_SyncIo_Tls_Prepare(GWEN_SYNCIO *sio)
422 {
423   GWEN_SYNCIO_TLS *xio;
424   int rv;
425   uint32_t lflags;
426   const char *custom_ciphers;
427   const char *errPos=NULL;
428 
429   assert(sio);
430   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
431   assert(xio);
432 
433   lflags=GWEN_SyncIo_GetFlags(sio);
434   DBG_INFO(GWEN_LOGDOMAIN, "Preparing SSL (%08x)", lflags);
435 
436   /* init session */
437   if (lflags & GWEN_SYNCIO_FLAGS_PASSIVE) {
438     DBG_INFO(GWEN_LOGDOMAIN, "Init as server");
439     rv=gnutls_init(&xio->session, GNUTLS_SERVER);
440   }
441   else {
442     DBG_INFO(GWEN_LOGDOMAIN, "Init as client");
443     rv=gnutls_init(&xio->session, GNUTLS_CLIENT);
444   }
445   if (rv) {
446     DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_init: %d (%s)", rv, gnutls_strerror(rv));
447     return GWEN_ERROR_GENERIC;
448   }
449 
450   /* set cipher priorities */
451   custom_ciphers=getenv("GWEN_TLS_CIPHER_PRIORITIES");
452   /* TODO: make custom ciphers configurable as priority string? */
453   if (custom_ciphers && *custom_ciphers) { /* use cipher list from env var */
454     GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Info, I18N("TLS: SSL cipher priority list: %s"), custom_ciphers);
455     rv=gnutls_priority_set_direct(xio->session, custom_ciphers, &errPos);
456     if (rv!=GNUTLS_E_SUCCESS) {
457       DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_priority_set_direct using '%s' failed: %s (%d) [%s]",
458                 custom_ciphers, gnutls_strerror(rv), rv, errPos?errPos:"");
459       gnutls_deinit(xio->session);
460       return GWEN_ERROR_GENERIC;
461     }
462   }
463   else {   /* use default ciphers from GnuTLS */
464     GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Notice, I18N("Using GnuTLS default ciphers."));
465     rv=gnutls_set_default_priority(xio->session);
466     if (rv!=GNUTLS_E_SUCCESS) {
467       DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_set_default_priority failed: %s (%d)", gnutls_strerror(rv), rv);
468       gnutls_deinit(xio->session);
469       return GWEN_ERROR_GENERIC;
470     }
471   }
472 
473   /* protect against too-many-known-ca problem */
474   gnutls_handshake_set_max_packet_length(xio->session, 64*1024);
475 
476   /* let a server request peer certs */
477   if ((lflags & GWEN_SYNCIO_FLAGS_PASSIVE) &&
478       (lflags & GWEN_SYNCIO_TLS_FLAGS_REQUEST_CERT))
479     gnutls_certificate_server_set_request(xio->session, GNUTLS_CERT_REQUIRE);
480 
481   /* prepare cert credentials */
482   rv=gnutls_certificate_allocate_credentials(&xio->credentials);
483   if (rv) {
484     DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_certificate_allocate_credentials: %d (%s)", rv, gnutls_strerror(rv));
485     gnutls_deinit(xio->session);
486     return GWEN_ERROR_GENERIC;
487   }
488 
489   /* possibly set key file and cert file */
490   if (xio->localCertFile && xio->localKeyFile) {
491     rv=gnutls_certificate_set_x509_key_file(xio->credentials,
492                                             xio->localCertFile,
493                                             xio->localKeyFile,
494                                             GNUTLS_X509_FMT_PEM);
495     if (rv<0) {
496       if (rv) {
497         DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_certificate_set_x509_key_file: %d (%s)", rv, gnutls_strerror(rv));
498         gnutls_certificate_free_credentials(xio->credentials);
499         gnutls_deinit(xio->session);
500         return GWEN_ERROR_GENERIC;
501       }
502     }
503   }
504 
505   /* find default trust file if none is selected */
506   if (lflags & GWEN_SYNCIO_TLS_FLAGS_ADD_TRUSTED_CAS) {
507 #if GWEN_TLS_USE_SYSTEM_CERTIFICATES
508     /* disable setting of default trust file as discussed on aqbanking-users.
509      * The rationale is that without this file being set gnutls should behave
510      * correctly on each system.
511      * On Linux systems it should use the standard mechanism of the underlying
512      * distribution. On Windows the default CA store should be used (if given
513      * "--with-default-trust-store-file" to "./configure" of GNUTLS).
514      */
515     int trustFileSet=0;
516 
517 
518     if (trustFileSet==0) {
519       /* Adds the system's default trusted CAs in order to verify client or server certificates. */
520       rv=gnutls_certificate_set_x509_system_trust(xio->credentials);
521       if (rv<=0) {
522         DBG_WARN(GWEN_LOGDOMAIN, "gnutls_certificate_set_x509_system_trust: %d (%s)", rv, gnutls_strerror(rv));
523       }
524       else {
525         DBG_INFO(GWEN_LOGDOMAIN, "Added %d default trusted certs from system", rv);
526         trustFileSet=1;
527       }
528     }
529 
530     /* try to find OpenSSL certificates */
531 # ifdef OS_WIN32
532     if (trustFileSet==0) {
533       char defaultPath[2*MAX_PATH+1];
534       const char *defaultFile = "ca-bundle.crt";
535       GWEN_STRINGLIST *paths;
536       GWEN_BUFFER *nbuf;
537 
538       if (GWEN_Directory_GetPrefixDirectory(defaultPath, sizeof(defaultPath))) {
539         DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_certificate_set_x509_key_file: could not get install prefix");
540         return GWEN_ERROR_GENERIC;
541       }
542       if (strcat_s(defaultPath, sizeof(defaultPath), "\\share\\gwenhywfar")) {
543         DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_certificate_set_x509_key_file: no memory on creating search path");
544         return GWEN_ERROR_GENERIC;
545       }
546 
547       paths=GWEN_StringList_new();
548       GWEN_StringList_AppendString(paths, defaultPath, 0, 0);
549 
550       nbuf=GWEN_Buffer_new(0, 256, 0, 1);
551       rv=GWEN_Directory_FindFileInPaths(paths, defaultFile, nbuf);
552       GWEN_StringList_free(paths);
553       if (rv==0) {
554         DBG_INFO(GWEN_LOGDOMAIN,
555                  "Using default ca-bundle from [%s]",
556                  GWEN_Buffer_GetStart(nbuf));
557 
558         rv=gnutls_certificate_set_x509_trust_file(xio->credentials,
559                                                   GWEN_Buffer_GetStart(nbuf),
560                                                   GNUTLS_X509_FMT_PEM);
561         if (rv<=0) {
562           DBG_WARN(GWEN_LOGDOMAIN,
563                    "gnutls_certificate_set_x509_trust_file(%s): %d (%s)",
564                    GWEN_Buffer_GetStart(nbuf), rv, gnutls_strerror(rv));
565         }
566         else {
567           DBG_INFO(GWEN_LOGDOMAIN,
568                    "Added %d trusted certs from [%s]", rv, GWEN_Buffer_GetStart(nbuf));
569           trustFileSet=1;
570         }
571       }
572       GWEN_Buffer_free(nbuf);
573     }
574 # endif
575 
576 
577 # ifndef OS_WIN32
578     /* try to finde certificate bundle */
579     if (trustFileSet==0) {
580       int i;
581       const char *sCertFile=NULL;
582 
583       for (i=0; ; i++) {
584         sCertFile=SYNCIO_TLS_SYSTEM_CERTFILES[i];
585         if (sCertFile==NULL)
586           break;
587         if (0==GWEN_Directory_GetPath(sCertFile, GWEN_PATH_FLAGS_NAMEMUSTEXIST | GWEN_PATH_FLAGS_VARIABLE)) {
588           DBG_INFO(GWEN_LOGDOMAIN, "Found system-wide cert bundle in %s", sCertFile);
589           break;
590         }
591       }
592 
593       if (sCertFile && *sCertFile) {
594         rv=gnutls_certificate_set_x509_trust_file(xio->credentials, sCertFile, GNUTLS_X509_FMT_PEM);
595         if (rv<=0) {
596           DBG_WARN(GWEN_LOGDOMAIN, "gnutls_certificate_set_x509_trust_file(%s): %d (%s)", sCertFile, rv, gnutls_strerror(rv));
597         }
598         else {
599           DBG_INFO(GWEN_LOGDOMAIN, "Added %d trusted certs from [%s]", rv, sCertFile);
600           trustFileSet=1;
601         }
602       }
603       else {
604         DBG_ERROR(GWEN_LOGDOMAIN, "No system-wide certificate bundle found.");
605       }
606     }
607 
608     /* try to find ca-certificates (at least available on Debian systems) */
609     if (trustFileSet==0) {
610       rv=GWEN_Directory_GetPath("/usr/share/ca-certificates", GWEN_PATH_FLAGS_NAMEMUSTEXIST);
611       if (rv>=0) {
612         rv=GWEN_SyncIo_Tls_AddCaCertFolder(sio, "/usr/share/ca-certificates");
613         if (rv<=0) {
614           DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
615         }
616         else {
617           trustFileSet=1;
618         }
619       }
620     }
621 
622 # endif
623 
624 
625     if (trustFileSet==0) {
626 
627       /* TODO: use gnutls_certificate_set_x509_system_trust() */
628       trustFileSet=1;
629     }
630 
631 
632 
633     if (trustFileSet==0) {
634       DBG_WARN(GWEN_LOGDOMAIN, "No default bundle file found");
635     }
636 #endif
637   }
638 
639   /* possibly set trust file */
640   if (xio->localTrustFile) {
641     rv=gnutls_certificate_set_x509_trust_file(xio->credentials,
642                                               xio->localTrustFile,
643                                               GNUTLS_X509_FMT_PEM);
644     if (rv<=0) {
645       DBG_ERROR(GWEN_LOGDOMAIN,
646                 "gnutls_certificate_set_x509_trust_file(%s): %d (%s)",
647                 (xio->localTrustFile)?(xio->localTrustFile):"-none-",
648                 rv, gnutls_strerror(rv));
649       gnutls_certificate_free_credentials(xio->credentials);
650       gnutls_deinit(xio->session);
651       return GWEN_ERROR_GENERIC;
652     }
653     else {
654       DBG_INFO(GWEN_LOGDOMAIN,
655                "Added %d trusted certs", rv);
656     }
657   }
658 
659   /* possibly set DH params */
660   if (xio->dhParamFile) {
661     GWEN_BUFFER *dbuf;
662 
663     dbuf=GWEN_Buffer_new(0, 256, 0, 1);
664     rv=GWEN_SyncIo_Tls__readFile(xio->dhParamFile, dbuf);
665     if (rv) {
666       DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
667       GWEN_Buffer_free(dbuf);
668       gnutls_certificate_free_credentials(xio->credentials);
669       gnutls_deinit(xio->session);
670       return rv;
671     }
672     else {
673       gnutls_datum_t d;
674       gnutls_dh_params_t dh_params=NULL;
675 
676       rv=gnutls_dh_params_init(&dh_params);
677       if (rv<0) {
678         GWEN_Buffer_free(dbuf);
679         DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_dh_params_init: %d (%s)", rv, gnutls_strerror(rv));
680         gnutls_certificate_free_credentials(xio->credentials);
681         gnutls_deinit(xio->session);
682         return GWEN_ERROR_GENERIC;
683       }
684 
685       d.size=GWEN_Buffer_GetUsedBytes(dbuf);
686       d.data=(unsigned char *)GWEN_Buffer_GetStart(dbuf);
687 
688       rv=gnutls_dh_params_import_pkcs3(dh_params, &d, GNUTLS_X509_FMT_PEM);
689       if (rv<0) {
690         GWEN_Buffer_free(dbuf);
691         DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_dh_params_import_pkcs3: %d (%s)", rv, gnutls_strerror(rv));
692         gnutls_certificate_free_credentials(xio->credentials);
693         gnutls_deinit(xio->session);
694         return GWEN_ERROR_GENERIC;
695       }
696       GWEN_Buffer_free(dbuf);
697 
698       gnutls_certificate_set_dh_params(xio->credentials, dh_params);
699     }
700   }
701 
702   /* set credentials in TLS session */
703   rv=gnutls_credentials_set(xio->session, GNUTLS_CRD_CERTIFICATE, xio->credentials);
704   if (rv<0) {
705     DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_credentials_set: %d (%s)", rv, gnutls_strerror(rv));
706     gnutls_certificate_free_credentials(xio->credentials);
707     gnutls_deinit(xio->session);
708     return GWEN_ERROR_GENERIC;
709   }
710 
711   /* if hostname set try to set it */
712   if (xio->hostName) {
713     rv=gnutls_server_name_set(xio->session, GNUTLS_NAME_DNS, xio->hostName, strlen(xio->hostName));
714     if (rv!=GNUTLS_E_SUCCESS) {
715       DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_server_name_set: %d (%s), ignoring", rv, gnutls_strerror(rv));
716     }
717   }
718 
719   /* we use our own push/pull functions */
720   gnutls_transport_set_ptr(xio->session, (gnutls_transport_ptr_t)sio);
721   gnutls_transport_set_push_function(xio->session, GWEN_SyncIo_Tls_Push);
722   gnutls_transport_set_pull_function(xio->session, GWEN_SyncIo_Tls_Pull);
723 #if GNUTLS_VERSION_NUMBER < 0x020c00
724   /* This function must be set to 0 in GNUTLS versions < 2.12.0 because we use
725    * custom push/pull functions.
726    * In GNUTLS 2.12.x this is set to 0 and since version 3 this functions is removed
727    * completely.
728    * So we only call this function now for GNUTLS < 2.12.0.
729    */
730   gnutls_transport_set_lowat(xio->session, 0);
731 #endif
732 
733   xio->prepared=1;
734 
735   return 0;
736 }
737 
738 
739 
GWEN_SyncIo_Tls_UndoPrepare(GWEN_SYNCIO * sio)740 void GWEN_SyncIo_Tls_UndoPrepare(GWEN_SYNCIO *sio)
741 {
742   GWEN_SYNCIO_TLS *xio;
743 
744   assert(sio);
745   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
746   assert(xio);
747 
748   if (xio->prepared) {
749     gnutls_certificate_free_credentials(xio->credentials);
750     gnutls_deinit(xio->session);
751     xio->prepared=0;
752   }
753 }
754 
755 
756 
GWEN_SyncIo_Tls_GetPeerCert(GWEN_SYNCIO * sio)757 int GWEN_SyncIo_Tls_GetPeerCert(GWEN_SYNCIO *sio)
758 {
759   GWEN_SYNCIO_TLS *xio;
760   const gnutls_datum_t *cert_list;
761   unsigned int cert_list_size;
762   size_t size;
763   GWEN_SSLCERTDESCR *certDescr;
764   char buffer1[64];
765   time_t t0;
766   int rv;
767   uint32_t lflags;
768   uint32_t errFlags=0;
769   int i;
770   unsigned int status;
771   GWEN_BUFFER *sbuf=NULL;
772 
773   assert(sio);
774   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
775   assert(xio);
776 
777   lflags=GWEN_SyncIo_GetFlags(sio);
778 
779   if (xio->peerCertDescr) {
780     GWEN_SslCertDescr_free(xio->peerCertDescr);
781     xio->peerCertDescr=NULL;
782   }
783   xio->peerCertFlags=0;
784 
785   t0=time(NULL);
786   if (t0<0) {
787     DBG_WARN(GWEN_LOGDOMAIN, "Could not get system time");
788     errFlags|=GWEN_SSL_CERT_FLAGS_SYSTEM;
789   }
790 
791   /* create new cert description, check cert on the fly */
792   certDescr=GWEN_SslCertDescr_new();
793 
794   /* some general tests */
795   if (lflags & GWEN_SYNCIO_TLS_FLAGS_ALLOW_V1_CA_CRT)
796     gnutls_certificate_set_verify_flags(xio->credentials,
797                                         GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
798 
799   rv=gnutls_certificate_verify_peers2(xio->session, &status);
800   if (rv<0) {
801     DBG_INFO(GWEN_LOGDOMAIN, "gnutls_certificate_verify_peers2: %d (%s)", rv, gnutls_strerror(rv));
802     GWEN_SslCertDescr_free(certDescr);
803     return GWEN_ERROR_SSL_SECURITY;
804   }
805 
806   if (gnutls_certificate_type_get(xio->session)!=GNUTLS_CRT_X509) {
807     DBG_INFO(GWEN_LOGDOMAIN, "Certificate is not X.509");
808 
809     GWEN_SslCertDescr_free(certDescr);
810     return GWEN_ERROR_SSL_SECURITY;
811   }
812 
813   if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
814     DBG_INFO(GWEN_LOGDOMAIN, "Signer not found");
815     GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Warning,
816                          I18N("Signer not found"));
817     errFlags|=GWEN_SSL_CERT_FLAGS_SIGNER_NOT_FOUND;
818   }
819 
820   if (status & GNUTLS_CERT_INVALID) {
821     DBG_INFO(GWEN_LOGDOMAIN, "Certificate is not trusted");
822     GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Warning,
823                          I18N("Certificate is not trusted"));
824     errFlags|=GWEN_SSL_CERT_FLAGS_INVALID;
825   }
826 
827   if (status & GNUTLS_CERT_REVOKED) {
828     DBG_INFO(GWEN_LOGDOMAIN, "Certificate has been revoked");
829     GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Warning,
830                          I18N("Certificate has been revoked"));
831     errFlags|=GWEN_SSL_CERT_FLAGS_REVOKED;
832   }
833 
834   cert_list=gnutls_certificate_get_peers(xio->session, &cert_list_size);
835   if (cert_list==NULL || cert_list_size==0) {
836     DBG_INFO(GWEN_LOGDOMAIN, "No peer certificates found");
837     return GWEN_ERROR_NO_DATA;
838   }
839 
840   for (i=0; i<(int) cert_list_size; i++) {
841     gnutls_x509_crt_t cert;
842     time_t t;
843 
844     rv=gnutls_x509_crt_init(&cert);
845     if (rv!=0) {
846       DBG_INFO(GWEN_LOGDOMAIN, "gnutls_x509_crt_init: %d (%s)", rv, gnutls_strerror(rv));
847       return GWEN_ERROR_GENERIC;
848     }
849 
850     rv=gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER); /* TODO: shouldn't we use the index?? */
851     if (rv!=0) {
852       DBG_INFO(GWEN_LOGDOMAIN, "gnutls_x509_crt_import: %d (%s)", rv, gnutls_strerror(rv));
853       gnutls_x509_crt_deinit(cert);
854       return GWEN_ERROR_GENERIC;
855     }
856 
857     if (i==0) {
858       gnutls_datum_t n= {NULL, 0};
859       gnutls_datum_t e= {NULL, 0};
860 
861       /* get public key from cert, if any */
862       rv=gnutls_x509_crt_get_pk_rsa_raw(cert, &n, &e);
863       if (rv!=0) {
864         DBG_INFO(GWEN_LOGDOMAIN, "gnutls_x509_crt_get_pk_rsa_raw: %d (%s)", rv, gnutls_strerror(rv));
865       }
866       else {
867         GWEN_BUFFER *kbuf;
868 
869         DBG_INFO(GWEN_LOGDOMAIN, "Key stored within certificate, extracting (modlen=%d, explen=%d)",
870                  n.size, e.size);
871 
872         kbuf=GWEN_Buffer_new(0, 256, 0, 1);
873 
874         if (n.data && n.size) {
875           /* store public modulus */
876           GWEN_Text_ToHexBuffer((const char *)(n.data), n.size, kbuf, 0, 0, 0);
877           GWEN_SslCertDescr_SetPubKeyModulus(certDescr, GWEN_Buffer_GetStart(kbuf));
878           GWEN_Buffer_Reset(kbuf);
879         }
880 
881         if (e.data && e.size) {
882           /* store public exponent */
883           GWEN_Text_ToHexBuffer((const char *)(e.data), e.size, kbuf, 0, 0, 0);
884           GWEN_SslCertDescr_SetPubKeyExponent(certDescr, GWEN_Buffer_GetStart(kbuf));
885           GWEN_Buffer_Reset(kbuf);
886         }
887 
888         GWEN_Buffer_free(kbuf);
889         if (n.data)
890           gcry_free(n.data);
891         if (e.data)
892           gcry_free(e.data);
893       }
894 
895       /* get fingerprint (MD5) */
896       size=16;
897       rv=gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_MD5, buffer1, &size);
898       if (rv!=0) {
899         DBG_INFO(GWEN_LOGDOMAIN, "gnutls_x509_crt_get_fingerprint(MD5): %d (%s)", rv, gnutls_strerror(rv));
900         GWEN_SslCertDescr_free(certDescr);
901         gnutls_x509_crt_deinit(cert);
902         return GWEN_ERROR_GENERIC;
903       }
904       else {
905         GWEN_BUFFER *dbuf;
906 
907         dbuf=GWEN_Buffer_new(0, 256, 0, 1);
908         if (GWEN_Text_ToHexBuffer(/* GCC4 pointer-signedness fix: */ buffer1,
909                                                                      size, dbuf, 2, ':', 0)) {
910           DBG_ERROR(GWEN_LOGDOMAIN,
911                     "Could not convert fingerprint to hex");
912         }
913         else {
914           GWEN_SslCertDescr_SetFingerPrint(certDescr, GWEN_Buffer_GetStart(dbuf));
915         }
916         GWEN_Buffer_free(dbuf);
917       }
918 
919       /* get fingerprint (SHA1) */
920       size=sizeof(buffer1);
921       rv=gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA1, buffer1, &size);
922       if (rv!=0) {
923         DBG_INFO(GWEN_LOGDOMAIN, "gnutls_x509_crt_get_fingerprint(SHA1): %d (%s)", rv, gnutls_strerror(rv));
924         GWEN_SslCertDescr_free(certDescr);
925         gnutls_x509_crt_deinit(cert);
926         return GWEN_ERROR_GENERIC;
927       }
928       else {
929         GWEN_BUFFER *dbuf;
930 
931         dbuf=GWEN_Buffer_new(0, 256, 0, 1);
932         if (GWEN_Text_ToHexBuffer(/* GCC4 pointer-signedness fix: */ buffer1,
933                                                                      size, dbuf, 2, ':', 0)) {
934           DBG_ERROR(GWEN_LOGDOMAIN,
935                     "Could not convert fingerprint to hex");
936         }
937         else {
938           GWEN_SslCertDescr_SetFingerPrintSha1(certDescr, GWEN_Buffer_GetStart(dbuf));
939         }
940         GWEN_Buffer_free(dbuf);
941       }
942 
943       /* get fingerprint (SHA512) */
944       size=sizeof(buffer1);
945       rv=gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA512, buffer1, &size);
946       if (rv!=0) {
947         DBG_INFO(GWEN_LOGDOMAIN, "gnutls_x509_crt_get_fingerprint(SHA512): %d (%s)", rv, gnutls_strerror(rv));
948         GWEN_SslCertDescr_free(certDescr);
949         gnutls_x509_crt_deinit(cert);
950         return GWEN_ERROR_GENERIC;
951       }
952       else {
953         GWEN_BUFFER *dbuf;
954 
955         dbuf=GWEN_Buffer_new(0, 256, 0, 1);
956         if (GWEN_Text_ToHexBuffer(/* GCC4 pointer-signedness fix: */ buffer1,
957                                                                      size, dbuf, 2, ':', 0)) {
958           DBG_ERROR(GWEN_LOGDOMAIN,
959                     "Could not convert fingerprint to hex");
960         }
961         else {
962           GWEN_SslCertDescr_SetFingerPrintSha512(certDescr, GWEN_Buffer_GetStart(dbuf));
963         }
964         GWEN_Buffer_free(dbuf);
965       }
966 
967 
968       if (xio->hostName) {
969         DBG_INFO(GWEN_LOGDOMAIN, "Checking hostname [%s]", xio->hostName);
970         if (!gnutls_x509_crt_check_hostname(cert, xio->hostName)) {
971           DBG_WARN(GWEN_LOGDOMAIN,
972                    "Certificate was not issued for this host");
973           GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Warning,
974                                I18N("Certificate was not issued for this host"));
975           errFlags|=GWEN_SSL_CERT_FLAGS_BAD_HOSTNAME;
976         }
977         else {
978           DBG_INFO(GWEN_LOGDOMAIN, "Cert is for this server");
979         }
980       }
981       else {
982         DBG_WARN(GWEN_LOGDOMAIN,
983                  "Hostname is not set, unable to verify the sender");
984         GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Warning,
985                              I18N("No hostname to verify the sender!"));
986       }
987 
988     }
989 
990     /* get activation time */
991     t=gnutls_x509_crt_get_activation_time(cert);
992     if (t<0) {
993       DBG_INFO(GWEN_LOGDOMAIN, "gnutls_x509_crt_get_activation_time: %d (%s)", rv, gnutls_strerror(rv));
994       errFlags|=GWEN_SSL_CERT_FLAGS_BAD_DATA;
995     }
996     else {
997       if (t>t0) {
998         DBG_INFO(GWEN_LOGDOMAIN, "Cert is not yet active");
999         errFlags|=GWEN_SSL_CERT_FLAGS_NOT_ACTIVE;
1000       }
1001       if (i==0) {
1002         GWEN_TIME *ti;
1003 
1004         ti=GWEN_Time_fromSeconds(t);
1005         if (ti)
1006           GWEN_SslCertDescr_SetNotBefore(certDescr, ti);
1007         GWEN_Time_free(ti);
1008       }
1009     }
1010 
1011     /* get expiration time */
1012     t=gnutls_x509_crt_get_expiration_time(cert);
1013     if (t<0) {
1014       DBG_INFO(GWEN_LOGDOMAIN, "gnutls_x509_crt_get_expiration_time: %d (%s)", rv, gnutls_strerror(rv));
1015       errFlags|=GWEN_SSL_CERT_FLAGS_BAD_DATA;
1016     }
1017     else {
1018       if (t<t0) {
1019         DBG_INFO(GWEN_LOGDOMAIN, "Cert has expired");
1020         errFlags|=GWEN_SSL_CERT_FLAGS_EXPIRED;
1021       }
1022       if (i==0) {
1023         GWEN_TIME *ti;
1024 
1025         ti=GWEN_Time_fromSeconds(t);
1026         if (ti)
1027           GWEN_SslCertDescr_SetNotAfter(certDescr, ti);
1028         GWEN_Time_free(ti);
1029       }
1030     }
1031 
1032     if (i==0) {
1033       /* get owner information, but only for first cert */
1034       size=sizeof(buffer1)-1;
1035       rv=gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, buffer1, &size);
1036       if (rv==0) {
1037         GWEN_SslCertDescr_SetCommonName(certDescr, buffer1);
1038         if (xio->hostName && strcasecmp(xio->hostName, buffer1)!=0) {
1039           DBG_INFO(GWEN_LOGDOMAIN, "Owner of certificate does not match hostname");
1040           errFlags|=GWEN_SSL_CERT_FLAGS_BAD_HOSTNAME;
1041         }
1042       }
1043 
1044       size=sizeof(buffer1)-1;
1045       rv=gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, buffer1, &size);
1046       if (rv==0)
1047         GWEN_SslCertDescr_SetOrganizationName(certDescr, buffer1);
1048 
1049       size=sizeof(buffer1)-1;
1050       rv=gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0, buffer1, &size);
1051       if (rv==0)
1052         GWEN_SslCertDescr_SetOrganizationalUnitName(certDescr, buffer1);
1053 
1054       size=sizeof(buffer1)-1;
1055       rv=gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, buffer1, &size);
1056       if (rv==0)
1057         GWEN_SslCertDescr_SetLocalityName(certDescr, buffer1);
1058 
1059       size=sizeof(buffer1)-1;
1060       rv=gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, buffer1, &size);
1061       if (rv==0)
1062         GWEN_SslCertDescr_SetStateOrProvinceName(certDescr, buffer1);
1063 
1064       size=sizeof(buffer1)-1;
1065       rv=gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, buffer1, &size);
1066       if (rv==0)
1067         GWEN_SslCertDescr_SetCountryName(certDescr, buffer1);
1068     }
1069 
1070     gnutls_x509_crt_deinit(cert);
1071   }
1072 
1073   /* done */
1074   if (errFlags)
1075     GWEN_SslCertDescr_SetIsError(certDescr, 1);
1076   else
1077     errFlags|=GWEN_SSL_CERT_FLAGS_OK;
1078 
1079   sbuf=GWEN_Buffer_new(0, 256, 0, 1);
1080 
1081   if (errFlags & GWEN_SSL_CERT_FLAGS_SIGNER_NOT_FOUND) {
1082     if (GWEN_Buffer_GetUsedBytes(sbuf))
1083       GWEN_Buffer_AppendString(sbuf, "; ");
1084     GWEN_Buffer_AppendString(sbuf, I18N("Signer not found"));
1085   }
1086 
1087   if (errFlags & GWEN_SSL_CERT_FLAGS_INVALID) {
1088     if (GWEN_Buffer_GetUsedBytes(sbuf))
1089       GWEN_Buffer_AppendString(sbuf, "; ");
1090     GWEN_Buffer_AppendString(sbuf, I18N("Certificate is not trusted"));
1091   }
1092 
1093   if (errFlags & GWEN_SSL_CERT_FLAGS_REVOKED) {
1094     if (GWEN_Buffer_GetUsedBytes(sbuf))
1095       GWEN_Buffer_AppendString(sbuf, "; ");
1096     GWEN_Buffer_AppendString(sbuf, I18N("Certificate has been revoked"));
1097   }
1098 
1099   if (errFlags & GWEN_SSL_CERT_FLAGS_EXPIRED) {
1100     if (GWEN_Buffer_GetUsedBytes(sbuf))
1101       GWEN_Buffer_AppendString(sbuf, "; ");
1102     GWEN_Buffer_AppendString(sbuf, I18N("Certificate has expired"));
1103   }
1104 
1105   if (errFlags & GWEN_SSL_CERT_FLAGS_NOT_ACTIVE) {
1106     if (GWEN_Buffer_GetUsedBytes(sbuf))
1107       GWEN_Buffer_AppendString(sbuf, "; ");
1108     GWEN_Buffer_AppendString(sbuf, I18N("Certificate is not active yet"));
1109   }
1110 
1111   if (errFlags & GWEN_SSL_CERT_FLAGS_BAD_HOSTNAME) {
1112     if (GWEN_Buffer_GetUsedBytes(sbuf))
1113       GWEN_Buffer_AppendString(sbuf, "; ");
1114     GWEN_Buffer_AppendString(sbuf, I18N("Certificate owner does not match hostname"));
1115   }
1116 
1117   if (errFlags & GWEN_SSL_CERT_FLAGS_BAD_DATA) {
1118     if (GWEN_Buffer_GetUsedBytes(sbuf))
1119       GWEN_Buffer_AppendString(sbuf, "; ");
1120     GWEN_Buffer_AppendString(sbuf, I18N("Certificate contains invalid information"));
1121   }
1122 
1123   if (errFlags & GWEN_SSL_CERT_FLAGS_SYSTEM) {
1124     if (GWEN_Buffer_GetUsedBytes(sbuf))
1125       GWEN_Buffer_AppendString(sbuf, "; ");
1126     GWEN_Buffer_AppendString(sbuf, I18N("A system error occurred while checking the certificate"));
1127   }
1128 
1129   if (errFlags & GWEN_SSL_CERT_FLAGS_OK) {
1130     if (GWEN_Buffer_GetUsedBytes(sbuf))
1131       GWEN_Buffer_AppendString(sbuf, "; ");
1132     GWEN_Buffer_AppendString(sbuf, I18N("The certificate is valid"));
1133   }
1134 
1135   GWEN_SslCertDescr_SetStatusText(certDescr, GWEN_Buffer_GetStart(sbuf));
1136   GWEN_SslCertDescr_SetStatusFlags(certDescr, errFlags);
1137   GWEN_Buffer_free(sbuf);
1138 
1139 #if 0
1140   if (1) {
1141     GWEN_DB_NODE *dbTest;
1142 
1143     dbTest=GWEN_DB_Group_new("Cert");
1144     GWEN_SslCertDescr_toDb(certDescr, dbTest);
1145     GWEN_DB_Dump(dbTest, 2);
1146     GWEN_DB_Group_free(dbTest);
1147   }
1148 #endif
1149 
1150   xio->peerCertDescr=certDescr;
1151   xio->peerCertFlags=errFlags;
1152 
1153   return 0;
1154 }
1155 
1156 
1157 
GWEN_SyncIo_Tls_Pull(gnutls_transport_ptr_t p,void * buf,size_t len)1158 ssize_t GWEN_SyncIo_Tls_Pull(gnutls_transport_ptr_t p, void *buf, size_t len)
1159 {
1160   GWEN_SYNCIO *sio;
1161   GWEN_SYNCIO_TLS *xio;
1162   GWEN_SYNCIO *baseIo;
1163   int rv;
1164 
1165   sio=(GWEN_SYNCIO *) p;
1166   assert(sio);
1167   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
1168   assert(xio);
1169 
1170   DBG_VERBOUS(GWEN_LOGDOMAIN, "TLS PULL: %d bytes", (int)len);
1171   baseIo=GWEN_SyncIo_GetBaseIo(sio);
1172   assert(baseIo);
1173 
1174   rv=GWEN_SyncIo_Read(baseIo, buf, len);
1175   if (rv<0) {
1176     DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
1177     gnutls_transport_set_errno(xio->session, errno);
1178     return (ssize_t)-1;
1179   }
1180 
1181   gnutls_transport_set_errno(xio->session, 0);
1182   DBG_VERBOUS(GWEN_LOGDOMAIN, "TLS PULL: returning %d bytes", rv);
1183   /*GWEN_Text_DumpString(buf, rv, 2);*/
1184   return rv;
1185 }
1186 
1187 
1188 
GWEN_SyncIo_Tls_Push(gnutls_transport_ptr_t p,const void * buf,size_t len)1189 ssize_t GWEN_SyncIo_Tls_Push(gnutls_transport_ptr_t p, const void *buf, size_t len)
1190 {
1191   GWEN_SYNCIO *sio;
1192   GWEN_SYNCIO_TLS *xio;
1193   GWEN_SYNCIO *baseIo;
1194   int rv;
1195 
1196   sio=(GWEN_SYNCIO *) p;
1197   assert(sio);
1198   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
1199   assert(xio);
1200 
1201   DBG_VERBOUS(GWEN_LOGDOMAIN, "TLS PUSH: %d bytes", (int)len);
1202   baseIo=GWEN_SyncIo_GetBaseIo(sio);
1203   assert(baseIo);
1204 
1205   rv=GWEN_SyncIo_Write(baseIo, buf, len);
1206   if (rv<0) {
1207     DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
1208     gnutls_transport_set_errno(xio->session, errno);
1209     return (ssize_t)-1;
1210   }
1211 
1212   gnutls_transport_set_errno(xio->session, 0);
1213   DBG_VERBOUS(GWEN_LOGDOMAIN, "TLS PUSH: returning %d bytes", rv);
1214   /*GWEN_Text_DumpString(buf, rv, 2);*/
1215   return rv;
1216 }
1217 
1218 
1219 
GWEN_SyncIo_Tls_ShowCipherInfo(GWEN_SYNCIO * sio)1220 void GWEN_SyncIo_Tls_ShowCipherInfo(GWEN_SYNCIO *sio)
1221 {
1222   GWEN_SYNCIO_TLS *xio;
1223   const char *s;
1224   gnutls_kx_algorithm_t kx;
1225   GWEN_BUFFER *cbuf;
1226   GWEN_BUFFER *sbuf;
1227 
1228   assert(sio);
1229   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
1230   assert(xio);
1231 
1232   cbuf=GWEN_Buffer_new(0, 256, 0, 1);
1233   sbuf=GWEN_Buffer_new(0, 256, 0, 1);
1234 
1235   /* protocol */
1236   s=gnutls_protocol_get_name(gnutls_protocol_get_version(xio->session));
1237   if (s && *s) {
1238     if (GWEN_Buffer_GetUsedBytes(cbuf))
1239       GWEN_Buffer_AppendString(cbuf, " ");
1240     GWEN_Buffer_AppendString(cbuf, "Protocol: ");
1241     GWEN_Buffer_AppendString(cbuf, s);
1242 
1243     GWEN_Buffer_AppendString(sbuf, s);
1244   }
1245   GWEN_Buffer_AppendString(sbuf, ":");
1246 
1247   /* key exchange algorithm */
1248   kx=gnutls_kx_get(xio->session);
1249   s=gnutls_kx_get_name(kx);
1250   if (s && *s) {
1251     if (GWEN_Buffer_GetUsedBytes(cbuf))
1252       GWEN_Buffer_AppendString(cbuf, " ");
1253     GWEN_Buffer_AppendString(cbuf, "Key exchange algorithm: ");
1254     GWEN_Buffer_AppendString(cbuf, s);
1255     GWEN_Buffer_AppendString(sbuf, s);
1256   }
1257   GWEN_Buffer_AppendString(sbuf, "-");
1258 
1259   /* cipher */
1260   s=gnutls_cipher_get_name(gnutls_cipher_get(xio->session));
1261   if (s && *s) {
1262     if (GWEN_Buffer_GetUsedBytes(cbuf))
1263       GWEN_Buffer_AppendString(cbuf, " ");
1264     GWEN_Buffer_AppendString(cbuf, "cipher algorithm: ");
1265     GWEN_Buffer_AppendString(cbuf, s);
1266     GWEN_Buffer_AppendString(sbuf, s);
1267   }
1268   GWEN_Buffer_AppendString(sbuf, ":");
1269 
1270   /* MAC algorithm */
1271   s=gnutls_mac_get_name(gnutls_mac_get(xio->session));
1272   if (s && *s) {
1273     if (GWEN_Buffer_GetUsedBytes(cbuf))
1274       GWEN_Buffer_AppendString(cbuf, " ");
1275     GWEN_Buffer_AppendString(cbuf, "MAC algorithm: ");
1276     GWEN_Buffer_AppendString(cbuf, s);
1277     GWEN_Buffer_AppendString(sbuf, s);
1278   }
1279 
1280 
1281   DBG_NOTICE(GWEN_LOGDOMAIN, "%s", GWEN_Buffer_GetStart(cbuf));
1282   GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Info, I18N("TLS: SSL-Ciphers negotiated: %s"), GWEN_Buffer_GetStart(sbuf));
1283   GWEN_Buffer_free(cbuf);
1284   GWEN_Buffer_free(sbuf);
1285 
1286   /* possibly show warning */
1287   switch (gnutls_cipher_get(xio->session)) {
1288   case GNUTLS_CIPHER_ARCFOUR_128:
1289   case GNUTLS_CIPHER_3DES_CBC:
1290   case GNUTLS_CIPHER_AES_128_CBC:
1291   case GNUTLS_CIPHER_ARCFOUR_40:
1292   case GNUTLS_CIPHER_CAMELLIA_128_CBC:
1293     GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Error, I18N("TLS: Warning - The server has chosen unsafe SSL-Ciphers!"));
1294     break;
1295   case GNUTLS_CIPHER_AES_256_CBC:
1296   case GNUTLS_CIPHER_CAMELLIA_256_CBC:
1297   case GNUTLS_CIPHER_RC2_40_CBC:
1298   case GNUTLS_CIPHER_DES_CBC:
1299 #ifdef GNUTLS_CIPHER_AES_192_CBC
1300   case GNUTLS_CIPHER_AES_192_CBC: /* new in gnutls-2.9.8, so i.e. not available in gnutls-2.8.x */
1301 #endif
1302   default:
1303     break;
1304   }
1305 }
1306 
1307 
1308 
GWEN_SyncIo_Tls_Connect(GWEN_SYNCIO * sio)1309 int GWENHYWFAR_CB GWEN_SyncIo_Tls_Connect(GWEN_SYNCIO *sio)
1310 {
1311   GWEN_SYNCIO_TLS *xio;
1312   GWEN_SYNCIO *baseIo;
1313   int rv;
1314 
1315   assert(sio);
1316   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
1317   assert(xio);
1318 
1319   baseIo=GWEN_SyncIo_GetBaseIo(sio);
1320   assert(baseIo);
1321 
1322   if (GWEN_SyncIo_GetFlags(sio) & GWEN_SYNCIO_FLAGS_PASSIVE) {
1323     if (GWEN_SyncIo_GetStatus(baseIo)!=GWEN_SyncIo_Status_Connected) {
1324       DBG_ERROR(GWEN_LOGDOMAIN, "Base layer is not connected");
1325       return GWEN_ERROR_NOT_CONNECTED;
1326     }
1327   }
1328   else {
1329     DBG_INFO(GWEN_LOGDOMAIN, "Connecting base layer");
1330     rv=GWEN_SyncIo_Connect(baseIo);
1331     if (rv<0) {
1332       DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
1333       return rv;
1334     }
1335     DBG_INFO(GWEN_LOGDOMAIN, "Base layer connected");
1336   }
1337 
1338   rv=GWEN_SyncIo_Tls_Prepare(sio);
1339   if (rv<0) {
1340     DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
1341     GWEN_SyncIo_Disconnect(baseIo);
1342     return rv;
1343   }
1344 
1345   do {
1346     rv=gnutls_handshake(xio->session);
1347   }
1348   while (rv==GNUTLS_E_AGAIN || rv==GNUTLS_E_INTERRUPTED);
1349 
1350   if (rv) {
1351     DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_handshake: %d (%s) [%s]",
1352               rv, gnutls_strerror(rv), gnutls_error_is_fatal(rv)?"fatal":"non-fatal");
1353     if (rv==GNUTLS_E_UNEXPECTED_PACKET_LENGTH) {
1354       GWEN_Gui_ProgressLog(0,
1355                            GWEN_LoggerLevel_Error,
1356                            I18N("A TLS handshake error occurred. "
1357                                 "If you are using AqBanking you should "
1358                                 "consider enabling the option "
1359                                 "\"force SSLv3\" in the user settings "
1360                                 "dialog."));
1361     }
1362     else {
1363       GWEN_Gui_ProgressLog2(0,
1364                             GWEN_LoggerLevel_Error,
1365                             I18N("TLS Handshake Error: %d (%s)"),
1366                             rv,
1367                             gnutls_strerror(rv));
1368     }
1369     GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Disconnected);
1370     GWEN_SyncIo_Tls_UndoPrepare(sio);
1371     GWEN_SyncIo_Disconnect(baseIo);
1372     return GWEN_ERROR_SSL;
1373   }
1374   else {
1375     /* show session info */
1376     GWEN_SyncIo_Tls_ShowCipherInfo(sio);
1377 
1378     /* check certificate */
1379     GWEN_SyncIo_SubFlags(sio, GWEN_SYNCIO_TLS_FLAGS_SECURE);
1380     rv=GWEN_SyncIo_Tls_GetPeerCert(sio);
1381     if (rv<0) {
1382       if (GWEN_SyncIo_GetFlags(sio) & GWEN_SYNCIO_TLS_FLAGS_NEED_PEER_CERT) {
1383         DBG_ERROR(GWEN_LOGDOMAIN, "No peer certificate when needed, aborting connection");
1384         GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Disconnected);
1385         GWEN_SyncIo_Tls_UndoPrepare(sio);
1386         GWEN_SyncIo_Disconnect(baseIo);
1387         return GWEN_ERROR_SSL_SECURITY;
1388       }
1389       else {
1390         DBG_INFO(GWEN_LOGDOMAIN, "SSL connected (insecure)");
1391         GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Connected);
1392         return 0;
1393       }
1394     }
1395     else {
1396       /* present cert to the user */
1397       rv=GWEN_SyncIo_Tls_CheckCert(sio, xio->peerCertDescr);
1398       if (rv<0) {
1399         DBG_ERROR(GWEN_LOGDOMAIN, "Peer cert not accepted (%d), aborting", rv);
1400         GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Disconnected);
1401         GWEN_SyncIo_Tls_UndoPrepare(sio);
1402         GWEN_SyncIo_Disconnect(baseIo);
1403         return GWEN_ERROR_SSL_SECURITY;
1404       }
1405       else {
1406         DBG_INFO(GWEN_LOGDOMAIN, "SSL connected (secure)");
1407         GWEN_SyncIo_AddFlags(sio, GWEN_SYNCIO_TLS_FLAGS_SECURE);
1408         GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Connected);
1409         return 0;
1410       }
1411     }
1412   }
1413 }
1414 
1415 
1416 
GWEN_SyncIo_Tls_Disconnect(GWEN_SYNCIO * sio)1417 int GWENHYWFAR_CB GWEN_SyncIo_Tls_Disconnect(GWEN_SYNCIO *sio)
1418 {
1419   GWEN_SYNCIO_TLS *xio;
1420   GWEN_SYNCIO *baseIo;
1421   int rv;
1422 
1423   assert(sio);
1424   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
1425   assert(xio);
1426 
1427   baseIo=GWEN_SyncIo_GetBaseIo(sio);
1428   assert(baseIo);
1429 
1430   if (GWEN_SyncIo_GetStatus(sio)!=GWEN_SyncIo_Status_Connected) {
1431     DBG_INFO(GWEN_LOGDOMAIN, "Not connected");
1432     GWEN_SyncIo_Tls_UndoPrepare(sio);
1433     GWEN_SyncIo_Disconnect(baseIo);
1434     return GWEN_ERROR_NOT_CONNECTED;
1435   }
1436 
1437   do {
1438     rv=gnutls_bye(xio->session, GNUTLS_SHUT_RDWR);
1439   }
1440   while (rv==GNUTLS_E_AGAIN || rv==GNUTLS_E_INTERRUPTED);
1441 
1442   if (rv) {
1443     DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_bye: %d (%s)", rv, gnutls_strerror(rv));
1444     GWEN_Gui_ProgressLog2(0,
1445                           GWEN_LoggerLevel_Info,
1446                           I18N("Error on gnutls_bye: %d (%s)"),
1447                           rv,
1448                           gnutls_strerror(rv));
1449     GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Disconnected);
1450     GWEN_SyncIo_Tls_UndoPrepare(sio);
1451     GWEN_SyncIo_Disconnect(baseIo);
1452     return GWEN_ERROR_SSL;
1453   }
1454 
1455   GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Disconnected);
1456   GWEN_SyncIo_Tls_UndoPrepare(sio);
1457   GWEN_SyncIo_Disconnect(baseIo);
1458   return 0;
1459 }
1460 
1461 
1462 
GWEN_SyncIo_Tls_Read(GWEN_SYNCIO * sio,uint8_t * buffer,uint32_t size)1463 int GWENHYWFAR_CB GWEN_SyncIo_Tls_Read(GWEN_SYNCIO *sio,
1464                                        uint8_t *buffer,
1465                                        uint32_t size)
1466 {
1467   GWEN_SYNCIO_TLS *xio;
1468   GWEN_SYNCIO *baseIo;
1469   int rv;
1470 
1471   assert(sio);
1472   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
1473   assert(xio);
1474 
1475   baseIo=GWEN_SyncIo_GetBaseIo(sio);
1476   assert(baseIo);
1477 
1478   if (GWEN_SyncIo_GetStatus(sio)!=GWEN_SyncIo_Status_Connected) {
1479     DBG_INFO(GWEN_LOGDOMAIN, "Not connected");
1480     GWEN_SyncIo_Tls_UndoPrepare(sio);
1481     GWEN_SyncIo_Disconnect(baseIo);
1482     return GWEN_ERROR_NOT_CONNECTED;
1483   }
1484 
1485   do {
1486     rv=gnutls_record_recv(xio->session, buffer, size);
1487   }
1488   while (rv==GNUTLS_E_AGAIN || rv==GNUTLS_E_INTERRUPTED);
1489 
1490   if (rv<0) {
1491     DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_record_recv: %d (%s)", rv, gnutls_strerror(rv));
1492 #if 0
1493     GWEN_Gui_ProgressLog2(0,
1494                           GWEN_LoggerLevel_Error,
1495                           I18N("Error on gnutls_record_recv: %d (%s)"),
1496                           rv,
1497                           gnutls_strerror(rv));
1498 #endif
1499     GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Disconnected);
1500     GWEN_SyncIo_Tls_UndoPrepare(sio);
1501     GWEN_SyncIo_Disconnect(baseIo);
1502 #ifdef GNUTLS_E_PREMATURE_TERMINATION
1503     if (rv==GNUTLS_E_PREMATURE_TERMINATION) {
1504       if (GWEN_SyncIo_GetFlags(sio) & GWEN_SYNCIO_TLS_FLAGS_IGN_PREMATURE_CLOSE) {
1505         DBG_ERROR(GWEN_LOGDOMAIN, "Detected premature disconnect by server (violates specs!), ignoring.");
1506         return 0; /* report EOF */
1507       }
1508       else {
1509         DBG_ERROR(GWEN_LOGDOMAIN, "Detected premature disconnect by server (violates specs!)");
1510         return GWEN_ERROR_SSL_PREMATURE_CLOSE;
1511       }
1512     }
1513 #endif
1514     return GWEN_ERROR_SSL;
1515   }
1516 
1517 #ifdef GWEN_TLS_DEBUG
1518   DBG_ERROR(0, "Received this:");
1519   GWEN_Text_DumpString((const char *) buffer, rv, 2);
1520 #endif
1521 
1522   return rv;
1523 }
1524 
1525 
1526 
GWEN_SyncIo_Tls_Write(GWEN_SYNCIO * sio,const uint8_t * buffer,uint32_t size)1527 int GWENHYWFAR_CB GWEN_SyncIo_Tls_Write(GWEN_SYNCIO *sio,
1528                                         const uint8_t *buffer,
1529                                         uint32_t size)
1530 {
1531   GWEN_SYNCIO_TLS *xio;
1532   GWEN_SYNCIO *baseIo;
1533   int rv;
1534 
1535   assert(sio);
1536   xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
1537   assert(xio);
1538 
1539 #ifdef GWEN_TLS_DEBUG
1540   DBG_ERROR(0, "Sending this:");
1541   GWEN_Text_DumpString((const char *) buffer, size, 2);
1542 #endif
1543 
1544   baseIo=GWEN_SyncIo_GetBaseIo(sio);
1545   assert(baseIo);
1546 
1547   if (GWEN_SyncIo_GetStatus(sio)!=GWEN_SyncIo_Status_Connected) {
1548     DBG_INFO(GWEN_LOGDOMAIN, "Not connected");
1549     GWEN_SyncIo_Tls_UndoPrepare(sio);
1550     GWEN_SyncIo_Disconnect(baseIo);
1551     return GWEN_ERROR_NOT_CONNECTED;
1552   }
1553 
1554   do {
1555     rv=gnutls_record_send(xio->session, buffer, size);
1556   }
1557   while (rv==GNUTLS_E_AGAIN || rv==GNUTLS_E_INTERRUPTED);
1558 
1559   if (rv<0) {
1560     DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_record_send: %d (%s)", rv, gnutls_strerror(rv));
1561     GWEN_Gui_ProgressLog2(0,
1562                           GWEN_LoggerLevel_Error,
1563                           I18N("Error on gnutls_record_send: %d (%s)"),
1564                           rv,
1565                           gnutls_strerror(rv));
1566     GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Disconnected);
1567     GWEN_SyncIo_Tls_UndoPrepare(sio);
1568     GWEN_SyncIo_Disconnect(baseIo);
1569     return GWEN_ERROR_SSL;
1570   }
1571 
1572   return rv;
1573 }
1574 
1575 
1576 
1577 
1578 
1579 
1580 
1581