On Mon, Oct 01, 2007 at 03:16:13PM +0200, Magnus Hagander wrote:
> On Mon, Oct 01, 2007 at 02:37:44PM +0200, Magnus Hagander wrote:
> > On Sat, Sep 29, 2007 at 09:01:16PM +0100, Dave Page wrote:
> > > Tom Lane wrote:
> > > > "Dave Page" <[EMAIL PROTECTED]> writes:
> > > >>> From: Tom Lane <[EMAIL PROTECTED]>
> > > >>> ... It's not entirely clear whether BIO_new_fp() would avoid the
> > > >>> problematic calls, but it doesn't look like it'd be hard to try.
> > > > 
> > > >> The last version of the patch I posted uses BIO_new_file() in all 
> > > >> cases, and (from memory) BIO_get_fp() in the non-win32 case to get a 
> > > >> FILE* to pass to fstat.
> > > > 
> > > > Did you manage to get rid of the bogus-error-message problem that
> > > > afflicted the first version of the patch?  If so, this way is fine.
> > > 
> > > No, thats still an issue.
> > 
> > A guess on this - probably the BIO stuff overwrites some internal OpenSSL
> > "errno" value, causing the wrong error to be passed up. Most likely, it's
> > not save to call BIO functions from inside the callback. My bet is that
> > it'll actually break without this patch, if you stick something that's
> > invalid in there. It's just taht we picked up the "does not exist" error
> > without calling BIO functions.
> > 
> > A quick peek at the OpenSSL sources seems to confirm this.
> > 
> > I think we want to either attempt to load the client certificate before we
> > connect (and before it's requested) and just queue up the error to show it
> > in only if it's requested, or we want to try some magic around
> > ERR_set_mark()/ERR_pop_to_mark() to clear out any BIO errors before we hand
> > control back.
> > 
> > I'll see if I can put together a poc patch - need to reproduce the problem
> > first :-)

Yes, that was the problem. Attached patch fixes the problem for me on
Windows and Linux using the error mark functionality. It seems a lot
cleaner than the other option.

Dave - can you test this one? Assuming that works, I'll go ahead and apply
it.

I also think we have the same problem in the current code, just not for
fopen(). We trap fopen() without anything happening inside OpenSSL, but if
we get an error in the OpenSSL functions we call, something similar would
likely happen. 

Should we backpatch all the stuff, or just the error push/pop thing?

//Magnus
Index: src/interfaces/libpq/fe-secure.c
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/fe-secure.c,v
retrieving revision 1.94
diff -c -r1.94 fe-secure.c
*** src/interfaces/libpq/fe-secure.c    16 Feb 2007 17:07:00 -0000      1.94
--- src/interfaces/libpq/fe-secure.c    1 Oct 2007 13:21:43 -0000
***************
*** 111,116 ****
--- 111,117 ----
  
  #ifdef USE_SSL
  #include <openssl/ssl.h>
+ #include <openssl/bio.h>
  #if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
  #include <openssl/conf.h> 
  #endif
***************
*** 567,572 ****
--- 568,577 ----
   *    This callback is only called when the server wants a
   *    client cert.
   *
+  *    Since BIO functions can set OpenSSL error codes, we must
+  *    reset the OpenSSL error stack on *every* exit from this
+  *    function once we've started using BIO.
+  *
   *    Must return 1 on success, 0 on no data or error.
   */
  static int
***************
*** 579,586 ****
        struct stat buf2;
  #endif
        char            fnbuf[MAXPGPATH];
!       FILE       *fp;
!       PGconn     *conn = (PGconn *) SSL_get_app_data(ssl);
        char            sebuf[256];
  
        if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
--- 584,592 ----
        struct stat buf2;
  #endif
        char            fnbuf[MAXPGPATH];
!       FILE            *fp;
!       BIO                     *bio;
!       PGconn          *conn = (PGconn *) SSL_get_app_data(ssl);
        char            sebuf[256];
  
        if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
***************
*** 590,605 ****
                return 0;
        }
  
        /* read the user certificate */
        snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
!       if ((fp = fopen(fnbuf, "r")) == NULL)
        {
                printfPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not open certificate file 
\"%s\": %s\n"),
                                                  fnbuf, pqStrerror(errno, 
sebuf, sizeof(sebuf)));
                return 0;
        }
!       if (PEM_read_X509(fp, x509, NULL, NULL) == NULL)
        {
                char       *err = SSLerrmessage();
  
--- 596,616 ----
                return 0;
        }
  
+       /* save OpenSSL error stack */
+       ERR_set_mark();
+ 
        /* read the user certificate */
        snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
!       if ((bio = BIO_new_file(fnbuf, "r")) == NULL)
        {
                printfPQExpBuffer(&conn->errorMessage,
                           libpq_gettext("could not open certificate file 
\"%s\": %s\n"),
                                                  fnbuf, pqStrerror(errno, 
sebuf, sizeof(sebuf)));
+               ERR_pop_to_mark();
                return 0;
        }
! 
!       if (PEM_read_bio_X509(bio, x509, NULL, NULL) == NULL)
        {
                char       *err = SSLerrmessage();
  
***************
*** 607,616 ****
                           libpq_gettext("could not read certificate file 
\"%s\": %s\n"),
                                                  fnbuf, err);
                SSLerrfree(err);
!               fclose(fp);
                return 0;
        }
!       fclose(fp);
  
  #if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
        if (getenv("PGSSLKEY"))
--- 618,629 ----
                           libpq_gettext("could not read certificate file 
\"%s\": %s\n"),
                                                  fnbuf, err);
                SSLerrfree(err);
!               BIO_free(bio);
!               ERR_pop_to_mark();
                return 0;
        }
! 
!       BIO_free(bio);
  
  #if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
        if (getenv("PGSSLKEY"))
***************
*** 625,630 ****
--- 638,644 ----
                {
                        printfPQExpBuffer(&conn->errorMessage,
                                libpq_gettext("invalid value of PGSSLKEY 
environment variable\n"));
+                       ERR_pop_to_mark();
                        return 0;
                }
  
***************
*** 640,647 ****
                                                          engine_str, err);
                        SSLerrfree(err);
                        free(engine_str);
                        return 0;
!               }       
  
                *pkey = ENGINE_load_private_key(engine_ptr, engine_colon + 1,
                                                                                
NULL, NULL);
--- 654,662 ----
                                                          engine_str, err);
                        SSLerrfree(err);
                        free(engine_str);
+                       ERR_pop_to_mark();
                        return 0;
!               }
  
                *pkey = ENGINE_load_private_key(engine_ptr, engine_colon + 1,
                                                                                
NULL, NULL);
***************
*** 654,661 ****
                                                          engine_colon + 1, 
engine_str, err);
                        SSLerrfree(err);
                        free(engine_str);
                        return 0;
!               }               
                free(engine_str);
        }
        else
--- 669,677 ----
                                                          engine_colon + 1, 
engine_str, err);
                        SSLerrfree(err);
                        free(engine_str);
+                       ERR_pop_to_mark();
                        return 0;
!               }
                free(engine_str);
        }
        else
***************
*** 668,673 ****
--- 684,690 ----
                        printfPQExpBuffer(&conn->errorMessage,
                                                        
libpq_gettext("certificate present, but not private key file \"%s\"\n"),
                                                        fnbuf);
+                       ERR_pop_to_mark();
                        return 0;
                }
  #ifndef WIN32
***************
*** 677,702 ****
                        printfPQExpBuffer(&conn->errorMessage,
                                libpq_gettext("private key file \"%s\" has 
wrong permissions\n"),
                                                        fnbuf);
                        return 0;
                }
  #endif
!               if ((fp = fopen(fnbuf, "r")) == NULL)
                {
                        printfPQExpBuffer(&conn->errorMessage,
                                libpq_gettext("could not open private key file 
\"%s\": %s\n"),
                                                        fnbuf, 
pqStrerror(errno, sebuf, sizeof(sebuf)));
                        return 0;
                }
  #ifndef WIN32
                if (fstat(fileno(fp), &buf2) == -1 ||
                        buf.st_dev != buf2.st_dev || buf.st_ino != buf2.st_ino)
                {
                        printfPQExpBuffer(&conn->errorMessage,
                                                        libpq_gettext("private 
key file \"%s\" changed during execution\n"), fnbuf);
                        return 0;
                }
  #endif
!               if (PEM_read_PrivateKey(fp, pkey, NULL, NULL) == NULL)
                {
                        char       *err = SSLerrmessage();
  
--- 694,725 ----
                        printfPQExpBuffer(&conn->errorMessage,
                                libpq_gettext("private key file \"%s\" has 
wrong permissions\n"),
                                                        fnbuf);
+                       ERR_pop_to_mark();
                        return 0;
                }
  #endif
! 
!               if ((bio = BIO_new_file(fnbuf, "r")) == NULL)
                {
                        printfPQExpBuffer(&conn->errorMessage,
                                libpq_gettext("could not open private key file 
\"%s\": %s\n"),
                                                        fnbuf, 
pqStrerror(errno, sebuf, sizeof(sebuf)));
+                       ERR_pop_to_mark();
                        return 0;
                }
  #ifndef WIN32
+               BIO_get_fp(bio, &fp);
                if (fstat(fileno(fp), &buf2) == -1 ||
                        buf.st_dev != buf2.st_dev || buf.st_ino != buf2.st_ino)
                {
                        printfPQExpBuffer(&conn->errorMessage,
                                                        libpq_gettext("private 
key file \"%s\" changed during execution\n"), fnbuf);
+                       ERR_pop_to_mark();
                        return 0;
                }
  #endif
! 
!               if (PEM_read_bio_PrivateKey(bio, pkey, NULL, NULL) == NULL)
                {
                        char       *err = SSLerrmessage();
  
***************
*** 704,713 ****
                                libpq_gettext("could not read private key file 
\"%s\": %s\n"),
                                                        fnbuf, err);
                        SSLerrfree(err);
!                       fclose(fp);
                        return 0;
                }
!               fclose(fp);
        }
  
        /* verify that the cert and key go together */
--- 727,739 ----
                                libpq_gettext("could not read private key file 
\"%s\": %s\n"),
                                                        fnbuf, err);
                        SSLerrfree(err);
! 
!                       BIO_free(bio);
!                       ERR_pop_to_mark();
                        return 0;
                }
! 
!               BIO_free(bio);
        }
  
        /* verify that the cert and key go together */
***************
*** 719,727 ****
--- 745,756 ----
                                                  libpq_gettext("certificate 
does not match private key file \"%s\": %s\n"),
                                                  fnbuf, err);
                SSLerrfree(err);
+               ERR_pop_to_mark();
                return 0;
        }
  
+       ERR_pop_to_mark();
+ 
        return 1;
  }
  
---------------------------(end of broadcast)---------------------------
TIP 1: if posting/reading through Usenet, please send an appropriate
       subscribe-nomail command to [EMAIL PROTECTED] so that your
       message can get through to the mailing list cleanly

Reply via email to