1 /***************************************************************************
2     begin       : Fri Apr 21 2017
3     copyright   : (C) 2019 by Martin Preuss
4     email       : martin@libchipcard.de
5 
6  ***************************************************************************
7  * This file is part of the project "AqBanking".                           *
8  * Please see toplevel file COPYING of that project for license details.   *
9  ***************************************************************************/
10 
11 #ifdef HAVE_CONFIG_H
12 # include <config.h>
13 #endif
14 
15 #include "siotlsext_p.h"
16 
17 #include <gwenhywfar/gui.h>
18 #include <gwenhywfar/syncio_tls.h>
19 #include <gwenhywfar/debug.h>
20 
21 
22 
23 
24 /* ------------------------------------------------------------------------------------------------
25  * forward declarations
26  * ------------------------------------------------------------------------------------------------
27  */
28 
29 /** @return 0 if undecided, 1 if acceptable, negative on error */
30 static int _checkCert(GWEN_SYNCIO *sio, const GWEN_SSLCERTDESCR *cert);
31 static int _checkStoredUserCerts(AB_USER *u, const GWEN_SSLCERTDESCR *cert);
32 static int _checkAgainstStoredCert(const GWEN_SSLCERTDESCR *cert, GWEN_SSLCERTDESCR *storedCert, GWEN_DB_NODE *dbC);
33 static int _checkStoredUserResponse(GWEN_DB_NODE *dbC, const char *sFingerprint);
34 static int _checkAutoDecision(const GWEN_SSLCERTDESCR *cert);
35 static int _askUserAboutCert(GWEN_SYNCIO *sio, const GWEN_SSLCERTDESCR *cert);
36 static void _storeAccessDate(GWEN_DB_NODE *dbCert);
37 static void _storeCertAndUserResponseInUser(AB_USER *u, const GWEN_SSLCERTDESCR *cert, int response);
38 
39 
40 /* ------------------------------------------------------------------------------------------------
41  * implementations
42  * ------------------------------------------------------------------------------------------------
43  */
44 
45 
GWEN_INHERIT(GWEN_SYNCIO,AB_SIOTLS_EXT)46 GWEN_INHERIT(GWEN_SYNCIO, AB_SIOTLS_EXT)
47 
48 
49 
50 void AB_SioTlsExt_Extend(GWEN_SYNCIO *sio, AB_USER *u)
51 {
52   AB_SIOTLS_EXT *xsio;
53 
54   GWEN_NEW_OBJECT(AB_SIOTLS_EXT, xsio);
55   GWEN_INHERIT_SETDATA(GWEN_SYNCIO, AB_SIOTLS_EXT, sio, xsio, AB_SioTlsExt_FreeData);
56 
57   /* set data */
58   xsio->user=u;
59 
60   /* set callbacks */
61   xsio->oldCheckCertFn=GWEN_SyncIo_Tls_SetCheckCertFn(sio, AB_SioTlsExt_CheckCert);
62 }
63 
64 
65 
AB_SioTlsExt_Unextend(GWEN_SYNCIO * sio)66 void AB_SioTlsExt_Unextend(GWEN_SYNCIO *sio)
67 {
68   AB_SIOTLS_EXT *xsio;
69 
70   assert(sio);
71   xsio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, AB_SIOTLS_EXT, sio);
72   assert(xsio);
73 
74   /* reset callbacks which point into AB_SioTlsExt */
75   GWEN_SyncIo_Tls_SetCheckCertFn(sio, xsio->oldCheckCertFn);
76 
77   /* unlink from GWEN_SYNCIO object */
78   DBG_INFO(AQBANKING_LOGDOMAIN, "Unlinking SIO from banking object");
79   GWEN_INHERIT_UNLINK(GWEN_SYNCIO, AB_SIOTLS_EXT, sio);
80 }
81 
82 
83 
AB_SioTlsExt_FreeData(void * bp,void * p)84 void GWENHYWFAR_CB AB_SioTlsExt_FreeData(void *bp, void *p)
85 {
86   AB_SIOTLS_EXT *xsio;
87 
88   xsio=(AB_SIOTLS_EXT *) p;
89   assert(xsio);
90   GWEN_FREE_OBJECT(xsio);
91 }
92 
93 
94 
95 /*
96 AB_USER *AB_SioTlsExt_GetUser(const GWEN_SYNCIO *sio) {
97   AB_SIOTLS_EXT *xsio;
98 
99   assert(sio);
100   xsio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, AB_SIOTLS_EXT, sio);
101   assert(xsio);
102 
103   return xsio->user;
104 }
105 */
106 
107 
108 
109 
110 
AB_SioTlsExt_CheckCert(GWEN_SYNCIO * sio,const GWEN_SSLCERTDESCR * cert)111 int GWENHYWFAR_CB AB_SioTlsExt_CheckCert(GWEN_SYNCIO *sio, const GWEN_SSLCERTDESCR *cert)
112 {
113   int rv;
114 
115   rv=_checkCert(sio, cert);
116   if (rv==1) {
117     DBG_INFO(AQBANKING_LOGDOMAIN, "Cert accepted.");
118     return 0;
119   }
120   else if (rv<0) {
121     DBG_INFO(AQBANKING_LOGDOMAIN, "here (%d)", rv);
122     return rv;
123   }
124 
125   /* undecided, abort */
126   DBG_INFO(AQBANKING_LOGDOMAIN, "Undecided, assuming abort");
127   return GWEN_ERROR_USER_ABORTED;
128 }
129 
130 
131 
_checkCert(GWEN_SYNCIO * sio,const GWEN_SSLCERTDESCR * cert)132 int _checkCert(GWEN_SYNCIO *sio, const GWEN_SSLCERTDESCR *cert)
133 {
134   AB_SIOTLS_EXT *xsio;
135   int rv;
136 
137   assert(sio);
138   xsio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, AB_SIOTLS_EXT, sio);
139   assert(xsio);
140 
141   assert(xsio->user);
142 
143   rv=_checkStoredUserCerts(xsio->user, cert);
144   if (rv!=0) {
145     DBG_INFO(AQBANKING_LOGDOMAIN, "here (%d)", rv);
146     return rv;
147   }
148 
149   rv=_checkAutoDecision(cert);
150   if (rv!=0) {
151     DBG_INFO(AQBANKING_LOGDOMAIN, "here (%d)", rv);
152     return rv;
153   }
154 
155   rv=_askUserAboutCert(sio, cert);
156   if (rv!=0) {
157     DBG_INFO(AQBANKING_LOGDOMAIN, "here (%d)", rv);
158     _storeCertAndUserResponseInUser(xsio->user, cert, rv);
159     return rv;
160   }
161 
162   /* undecided */
163   return 0;
164 }
165 
166 
167 
_checkStoredUserCerts(AB_USER * u,const GWEN_SSLCERTDESCR * cert)168 int _checkStoredUserCerts(AB_USER *u, const GWEN_SSLCERTDESCR *cert)
169 {
170   GWEN_DB_NODE *dbCerts;
171   GWEN_DB_NODE *dbC;
172   const char *sFingerprint;
173 
174   assert(u);
175 
176   sFingerprint=GWEN_SslCertDescr_GetFingerPrint(cert);
177 
178   /* get or create user-based certificate store */
179   dbCerts=AB_User_GetCertDb(u);
180   if (dbCerts==NULL) {
181     dbCerts=GWEN_DB_Group_new("certs");
182     AB_User_SetCertDb(u, dbCerts);
183   }
184 
185   /* find group which contains the certificate with the given fingerprint */
186   dbC=GWEN_DB_GetGroup(dbCerts, GWEN_PATH_FLAGS_PATHMUSTEXIST, sFingerprint);
187   if (dbC) {
188     GWEN_SSLCERTDESCR *storedCert;
189     int rv;
190 
191     /* there is such a group, read stored certificate */
192     storedCert=GWEN_SslCertDescr_fromDb(dbC);
193     if (storedCert==NULL) {
194       DBG_ERROR(AQBANKING_LOGDOMAIN, "Unable to load stored certificate \"%s\"", sFingerprint);
195       return GWEN_ERROR_INTERNAL;
196     }
197 
198     _storeAccessDate(dbC);
199 
200     rv=_checkAgainstStoredCert(cert, storedCert, dbC);
201     DBG_INFO(AQBANKING_LOGDOMAIN, "here (%d)", rv);
202     GWEN_SslCertDescr_free(storedCert);
203     return rv;
204   } /* if dbC */
205 
206   /* undecided */
207   return 0;
208 }
209 
210 
211 
_checkAgainstStoredCert(const GWEN_SSLCERTDESCR * cert,GWEN_SSLCERTDESCR * storedCert,GWEN_DB_NODE * dbC)212 int _checkAgainstStoredCert(const GWEN_SSLCERTDESCR *cert, GWEN_SSLCERTDESCR *storedCert, GWEN_DB_NODE *dbC)
213 {
214   const char *sFingerprint;
215   const char *sStatus;
216   uint32_t iStatus;
217   uint32_t iStoredStatus;
218 
219   sFingerprint=GWEN_SslCertDescr_GetFingerPrint(cert);
220   sStatus=GWEN_SslCertDescr_GetStatusText(cert);
221   iStatus=GWEN_SslCertDescr_GetStatusFlags(cert);
222 
223   /* get status of stored certificate */
224   iStoredStatus=GWEN_SslCertDescr_GetStatusFlags(storedCert);
225 
226   /* compare status texts */
227   if (iStatus==iStoredStatus) {
228     /* found matching cert, return user's previous answer */
229     DBG_NOTICE(AQBANKING_LOGDOMAIN, "Found matching certificate \"%s\" with same status", sFingerprint);
230     return _checkStoredUserResponse(dbC, sFingerprint);
231   } /* if same status */
232   else {
233     DBG_NOTICE(AQBANKING_LOGDOMAIN,
234                "Status for certificate \%s\" has changed to \"%s\" (%08x->%08x), need to present",
235                sFingerprint, sStatus,
236                iStoredStatus, iStatus);
237   }
238   return 0;
239 }
240 
241 
242 
_checkStoredUserResponse(GWEN_DB_NODE * dbC,const char * sFingerprint)243 int _checkStoredUserResponse(GWEN_DB_NODE *dbC, const char *sFingerprint)
244 {
245   int rv;
246 
247   rv=GWEN_DB_GetIntValue(dbC, "userResponse", 0, -1);
248   if (rv==0) {
249     /* last user response was to accept the certificate so we're done */
250     DBG_NOTICE(AQBANKING_LOGDOMAIN, "Automatically accepting certificate [%s]", sFingerprint);
251     return 1;
252   }
253   else {
254     /* last user response was to reject the certificate so we're done */
255     /* DBG_NOTICE(AQBANKING_LOGDOMAIN, "Automatically rejecting certificate [%s] (%d)", sFingerprint, rv);
256      return rv; */
257     /* undecided (ask user again) */
258     return 0;
259   }
260 }
261 
262 
263 
_checkAutoDecision(const GWEN_SSLCERTDESCR * cert)264 int _checkAutoDecision(const GWEN_SSLCERTDESCR *cert)
265 {
266   GWEN_GUI *gui;
267   const char *sFingerprint;
268 
269   sFingerprint=GWEN_SslCertDescr_GetFingerPrint(cert);
270 
271   /* at this point the certificate was either not found or its status has changed,
272    * possibly ask the user how to preceed */
273   gui=GWEN_Gui_GetGui();
274   assert(gui);
275 
276   if (GWEN_Gui_GetFlags(gui) & GWEN_GUI_FLAGS_NONINTERACTIVE) {
277     uint32_t fl;
278 
279     fl=GWEN_SslCertDescr_GetStatusFlags(cert);
280     if (fl==GWEN_SSL_CERT_FLAGS_OK) {
281       if (GWEN_Gui_GetFlags(gui) & GWEN_GUI_FLAGS_ACCEPTVALIDCERTS) {
282         DBG_NOTICE(AQBANKING_LOGDOMAIN, "Automatically accepting valid new certificate [%s]", sFingerprint);
283         return 1;
284       }
285       else {
286         DBG_NOTICE(AQBANKING_LOGDOMAIN, "Automatically rejecting certificate [%s] (noninteractive)", sFingerprint);
287         GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Warning,
288                               "Automatically rejecting certificate [%s] (noninteractive)",
289                               sFingerprint);
290         return GWEN_ERROR_USER_ABORTED;
291       }
292     } /* if cert is valid */
293     else {
294       if (GWEN_Gui_GetFlags(gui) & GWEN_GUI_FLAGS_REJECTINVALIDCERTS) {
295         DBG_NOTICE(AQBANKING_LOGDOMAIN, "Automatically rejecting invalid certificate [%s] (noninteractive)", sFingerprint);
296         GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Warning,
297                               "Automatically rejecting invalid certificate [%s] (noninteractive)",
298                               sFingerprint);
299         return GWEN_ERROR_USER_ABORTED;
300       }
301     }
302   } /* if non-interactive */
303 
304   /* undecided */
305   return 0;
306 }
307 
308 
309 
_askUserAboutCert(GWEN_SYNCIO * sio,const GWEN_SSLCERTDESCR * cert)310 int _askUserAboutCert(GWEN_SYNCIO *sio, const GWEN_SSLCERTDESCR *cert)
311 {
312   AB_SIOTLS_EXT *xsio;
313 
314   assert(sio);
315   xsio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, AB_SIOTLS_EXT, sio);
316   assert(xsio);
317 
318   /* use previous checkCert function, which normally presents the certificate
319    * to the user and asks for a response */
320   if (xsio->oldCheckCertFn) {
321     int rv;
322 
323     /* get user response */
324     rv=xsio->oldCheckCertFn(sio, cert);
325     if (rv==0)
326       return 1;
327     else {
328       DBG_INFO(AQBANKING_LOGDOMAIN, "here (%d)", rv);
329       return rv;
330     }
331   }
332   else {
333     DBG_NOTICE(AQBANKING_LOGDOMAIN, "Internal error: No previous checkCert function");
334     return GWEN_ERROR_INTERNAL;
335   }
336 }
337 
338 
339 
_storeCertAndUserResponseInUser(AB_USER * u,const GWEN_SSLCERTDESCR * cert,int response)340 void _storeCertAndUserResponseInUser(AB_USER *u, const GWEN_SSLCERTDESCR *cert, int response)
341 {
342   GWEN_DB_NODE *dbCerts;
343   GWEN_DB_NODE *dbC;
344   const char *sFingerprint;
345   const char *sStatus;
346 
347   sFingerprint=GWEN_SslCertDescr_GetFingerPrint(cert);
348   sStatus=GWEN_SslCertDescr_GetStatusText(cert);
349 
350   dbCerts=AB_User_GetCertDb(u);
351   assert(dbCerts);
352 
353   /* store certificate in database */
354   dbC=GWEN_DB_GetGroup(dbCerts, GWEN_DB_FLAGS_OVERWRITE_GROUPS, sFingerprint);
355   assert(dbC);
356   GWEN_SslCertDescr_toDb(cert, dbC);
357 
358   /* store user response */
359   GWEN_DB_SetIntValue(dbC, GWEN_DB_FLAGS_OVERWRITE_VARS, "userResponse", (response==1)?0:response);
360   DBG_NOTICE(AQBANKING_LOGDOMAIN,
361              "User response to presentation of cert \"%s\" (%s): %d",
362              sFingerprint, sStatus, (response==1)?0:response);
363 
364   _storeAccessDate(dbC);
365 }
366 
367 
368 
_storeAccessDate(GWEN_DB_NODE * dbCert)369 void _storeAccessDate(GWEN_DB_NODE *dbCert)
370 {
371   GWEN_DATE *dt;
372 
373   dt=GWEN_Date_CurrentDate();
374   GWEN_DB_SetCharValue(dbCert, GWEN_DB_FLAGS_OVERWRITE_VARS, "lastAccessDate", GWEN_Date_GetString(dt));
375   GWEN_Date_free(dt);
376 }
377 
378