Fossil

Check-in [7a3bf55f54]
Login

Check-in [7a3bf55f54]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix the SSL-server code so that the "fossil ui --tls" command (and similar) now work on Windows.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 7a3bf55f5489f5348830c5f116d872c26207121fbfd916653463f189bfa754ab
User & Date: drh 2021-12-29 02:59:38.883
References
2022-01-27
04:34
Fix an off by 1 bug in "win32_http_request()" which was causing POST request to fail on windows. (e.g. wikiedit preview or upload of a file on the chat) Problem appear in checkin [7a3bf55f5489f534] ... (check-in: e4b49ce37d user: mgagnon tags: trunk)
Context
2021-12-29
03:15
Adapted ssl_new_server() docs to account for code changes. ... (check-in: 258479650b user: stephan tags: trunk)
02:59
Fix the SSL-server code so that the "fossil ui --tls" command (and similar) now work on Windows. ... (check-in: 7a3bf55f54 user: drh tags: trunk)
00:49
Update buildmsvc.bat after testing successful compilation with the Visual Studio 2022 toolchain. ... (check-in: 2ed7465cf7 user: danield tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/http_ssl.c.
737
738
739
740
741
742
743

744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765

766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
           || sslctx_use_pkey_from_mem(sslCtx, sslSelfPKey, -1) ){
      fossil_fatal("Error loading self-signed CERT");
    }
    if( !SSL_CTX_check_private_key(sslCtx) ){
      fossil_fatal("PRIVATE KEY \"%s\" does not match CERT \"%s\"",
           zKeyFile, zCertFile);
    }

    sslIsInit = 2;
  }else{
    assert( sslIsInit==2 );
  }
}

typedef struct SslServerConn {
  SSL *ssl;          /* The SSL codec */
  int atEof;         /* True when EOF reached. */
  int fd0;           /* Read channel, or socket */
  int fd1;           /* Write channel */
} SslServerConn;

/*
** Create a new server-side codec.  The arguments are the file
** descriptors from which teh codec reads and writes, respectively.
**
** If the writeFd is negative, then use then the readFd is a socket
** over which we both read and write.
*/
void *ssl_new_server(int readFd, int writeFd){
  SslServerConn *pServer = fossil_malloc_zero(sizeof(*pServer));

  pServer->ssl = SSL_new(sslCtx);
  pServer->fd0 = readFd;
  pServer->fd1 = writeFd;
  if( writeFd<0 ){
    SSL_set_fd(pServer->ssl, readFd);
  }else{
    SSL_set_rfd(pServer->ssl, readFd);
    SSL_set_wfd(pServer->ssl, writeFd);
  }
  SSL_accept(pServer->ssl);
  return (void*)pServer;
}

/*
** Close a server-side code previously returned from ssl_new_server().
*/
void ssl_close_server(void *pServerArg){
  SslServerConn *pServer = (SslServerConn*)pServerArg;
  SSL_free(pServer->ssl);
  close(pServer->fd0);
  if( pServer->fd1>=0 ) close(pServer->fd0);
  fossil_free(pServer);
}

/*
** Return TRUE if there are no more bytes available to be read from
** the client.
*/







>









<
|









|

>

|
|
<
|
<
<
<
<










<
<







737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753

754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769

770




771
772
773
774
775
776
777
778
779
780


781
782
783
784
785
786
787
           || sslctx_use_pkey_from_mem(sslCtx, sslSelfPKey, -1) ){
      fossil_fatal("Error loading self-signed CERT");
    }
    if( !SSL_CTX_check_private_key(sslCtx) ){
      fossil_fatal("PRIVATE KEY \"%s\" does not match CERT \"%s\"",
           zKeyFile, zCertFile);
    }
    SSL_CTX_set_mode(sslCtx, SSL_MODE_AUTO_RETRY);
    sslIsInit = 2;
  }else{
    assert( sslIsInit==2 );
  }
}

typedef struct SslServerConn {
  SSL *ssl;          /* The SSL codec */
  int atEof;         /* True when EOF reached. */

  int iSocket;       /* The socket */
} SslServerConn;

/*
** Create a new server-side codec.  The arguments are the file
** descriptors from which teh codec reads and writes, respectively.
**
** If the writeFd is negative, then use then the readFd is a socket
** over which we both read and write.
*/
void *ssl_new_server(int iSocket){
  SslServerConn *pServer = fossil_malloc_zero(sizeof(*pServer));
  BIO *b = BIO_new_socket(iSocket, 0);
  pServer->ssl = SSL_new(sslCtx);
  pServer->atEof = 0;
  pServer->iSocket = iSocket;

  SSL_set_bio(pServer->ssl, b, b);




  SSL_accept(pServer->ssl);
  return (void*)pServer;
}

/*
** Close a server-side code previously returned from ssl_new_server().
*/
void ssl_close_server(void *pServerArg){
  SslServerConn *pServer = (SslServerConn*)pServerArg;
  SSL_free(pServer->ssl);


  fossil_free(pServer);
}

/*
** Return TRUE if there are no more bytes available to be read from
** the client.
*/
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
*/
size_t ssl_read_server(void *pServerArg, char *zBuf, size_t nBuf){
  int n;
  SslServerConn *pServer = (SslServerConn*)pServerArg;
  if( pServer->atEof ) return 0;
  if( nBuf>0x7fffffff ){ fossil_fatal("SSL read too big"); }
  n = SSL_read(pServer->ssl, zBuf, (int)nBuf);
  if( n<nBuf ) pServer->atEof = 1;
  return n;
}

/*
** Read a single line of text from the client.
*/
char *ssl_gets(void *pServerArg, char *zBuf, int nBuf){
  int n = 0;







|
|







796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
*/
size_t ssl_read_server(void *pServerArg, char *zBuf, size_t nBuf){
  int n;
  SslServerConn *pServer = (SslServerConn*)pServerArg;
  if( pServer->atEof ) return 0;
  if( nBuf>0x7fffffff ){ fossil_fatal("SSL read too big"); }
  n = SSL_read(pServer->ssl, zBuf, (int)nBuf);
  if( n==0 ) pServer->atEof = 1;
  return n<=0 ? 0 : n;
}

/*
** Read a single line of text from the client.
*/
char *ssl_gets(void *pServerArg, char *zBuf, int nBuf){
  int n = 0;
834
835
836
837
838
839
840
841
842
843



844

845
846
847
848
849
850
851
/*
** Write cleartext bytes into the SSL server codec so that they can
** be encrypted and sent back to the client.
*/
size_t ssl_write_server(void *pServerArg, char *zBuf, size_t nBuf){
  int n;
  SslServerConn *pServer = (SslServerConn*)pServerArg;
  if( pServer->atEof ) return 0;
  if( nBuf>0x7fffffff ){ fossil_fatal("SSL write too big"); }
  n = SSL_write(pServer->ssl, zBuf, (int)nBuf);



  return n;

}

#endif /* FOSSIL_ENABLE_SSL */

/*
** COMMAND: tls-config*
** COMMAND: ssl-config







|


>
>
>
|
>







828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
/*
** Write cleartext bytes into the SSL server codec so that they can
** be encrypted and sent back to the client.
*/
size_t ssl_write_server(void *pServerArg, char *zBuf, size_t nBuf){
  int n;
  SslServerConn *pServer = (SslServerConn*)pServerArg;
  if( nBuf<=0 ) return 0;
  if( nBuf>0x7fffffff ){ fossil_fatal("SSL write too big"); }
  n = SSL_write(pServer->ssl, zBuf, (int)nBuf);
  if( n<=0 ){
    return -SSL_get_error(pServer->ssl, n);
  }else{
    return n;
  }
}

#endif /* FOSSIL_ENABLE_SSL */

/*
** COMMAND: tls-config*
** COMMAND: ssl-config
Changes to src/main.c.
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
  if( useSCGI ){
    cgi_handle_scgi_request();
  }else if( g.fSshClient & CGI_SSH_CLIENT ){
    ssh_request_loop(zIpAddr, glob_create(zFileGlob));
  }else{
#if FOSSIL_ENABLE_SSL
    if( g.httpUseSSL ){
      g.httpSSLConn = ssl_new_server(0,-1);
    }
#endif
    cgi_handle_http_request(zIpAddr);
  }
  process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
#if FOSSIL_ENABLE_SSL
  if( g.httpUseSSL && g.httpSSLConn ){







|







2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
  if( useSCGI ){
    cgi_handle_scgi_request();
  }else if( g.fSshClient & CGI_SSH_CLIENT ){
    ssh_request_loop(zIpAddr, glob_create(zFileGlob));
  }else{
#if FOSSIL_ENABLE_SSL
    if( g.httpUseSSL ){
      g.httpSSLConn = ssl_new_server(0);
    }
#endif
    cgi_handle_http_request(zIpAddr);
  }
  process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
#if FOSSIL_ENABLE_SSL
  if( g.httpUseSSL && g.httpSSLConn ){
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
      g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail);
    }
  }
  if( flags & HTTP_SERVER_SCGI ){
    cgi_handle_scgi_request();
  }else if( g.httpUseSSL ){
#if FOSSIL_ENABLE_SSL
    g.httpSSLConn = ssl_new_server(0,-1);
#endif
    cgi_handle_http_request(0);
  }else{
    cgi_handle_http_request(0);
  }
  process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
  if( g.fAnyTrace ){
    fprintf(stderr, "/***** Webpage finished in subprocess %d *****/\n",
            getpid());
  }
#if FOSSIL_ENABLE_SSL
  if( g.httpUseSSL && g.httpSSLConn ){
    ssl_close_server(g.httpSSLConn);
    g.httpSSLConn = 0;
  }
#endif /* FOSSIL_ENABLE_SSL */

#else /* WIN32 */
  /* Win32 implementation */
  if( g.httpUseSSL ){
    fossil_fatal("TLS-encrypted server is not (yet) supported on Windows");
  }
  if( allowRepoList ){
    flags |= HTTP_SERVER_REPOLIST;
  }
  if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){
    win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile,
                      zAltBase, zNotFound, zFileGlob, zIpAddr, flags);
  }







|



















<
<
<







3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310



3311
3312
3313
3314
3315
3316
3317
      g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail);
    }
  }
  if( flags & HTTP_SERVER_SCGI ){
    cgi_handle_scgi_request();
  }else if( g.httpUseSSL ){
#if FOSSIL_ENABLE_SSL
    g.httpSSLConn = ssl_new_server(0);
#endif
    cgi_handle_http_request(0);
  }else{
    cgi_handle_http_request(0);
  }
  process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
  if( g.fAnyTrace ){
    fprintf(stderr, "/***** Webpage finished in subprocess %d *****/\n",
            getpid());
  }
#if FOSSIL_ENABLE_SSL
  if( g.httpUseSSL && g.httpSSLConn ){
    ssl_close_server(g.httpSSLConn);
    g.httpSSLConn = 0;
  }
#endif /* FOSSIL_ENABLE_SSL */

#else /* WIN32 */
  /* Win32 implementation */



  if( allowRepoList ){
    flags |= HTTP_SERVER_REPOLIST;
  }
  if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){
    win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile,
                      zAltBase, zNotFound, zFileGlob, zIpAddr, flags);
  }
Changes to src/winhttp.c.
338
339
340
341
342
343
344

345
346
347
348
349

350
351
352
353
354
355
356
357





358





359
360

361
362
363
364
365
366
367
368
369
370





371
372

373
374
375
376
377





378
379

380
381
382
383
384
385
386
387
388
static void win32_http_request(void *pAppData){
  HttpRequest *p = (HttpRequest*)pAppData;
  FILE *in = 0, *out = 0, *aux = 0;
  int amt, got, i;
  int wanted = 0;
  char *z;
  char *zIp;

  char zCmdFName[MAX_PATH];
  char zRequestFName[MAX_PATH];
  char zReplyFName[MAX_PATH];
  char zCmd[2000];          /* Command-line to process the request */
  char zHdr[4000];          /* The HTTP request header */


  sqlite3_snprintf(MAX_PATH, zCmdFName,
                   "%s_%06d_cmd.txt", zTempPrefix, p->id);
  sqlite3_snprintf(MAX_PATH, zRequestFName,
                   "%s_%06d_in.txt", zTempPrefix, p->id);
  sqlite3_snprintf(MAX_PATH, zReplyFName,
                   "%s_%06d_out.txt", zTempPrefix, p->id);
  amt = 0;





  while( amt<sizeof(zHdr) ){





    got = recv(p->s, &zHdr[amt], sizeof(zHdr)-1-amt, 0);
    if( got==SOCKET_ERROR ) goto end_request;

    if( got==0 ){
      wanted = 0;
      break;
    }
    amt += got;
    zHdr[amt] = 0;
    z = strstr(zHdr, "\r\n\r\n");
    if( z ){
      wanted = find_content_length(zHdr) + (&z[4]-zHdr) - amt;
      break;





    }
  }

  if( amt>=sizeof(zHdr) ) goto end_request;
  out = fossil_fopen(zRequestFName, "wb");
  if( out==0 ) goto end_request;
  fwrite(zHdr, 1, amt, out);
  while( wanted>0 ){





    got = recv(p->s, zHdr, sizeof(zHdr), 0);
    if( got==SOCKET_ERROR ) goto end_request;

    if( got ){
      fwrite(zHdr, 1, got, out);
    }else{
      break;
    }
    wanted -= got;
  }

  /*







>




|
>








>
>
>
>
>
|
>
>
>
>
>
|
|
>





|
|

|

>
>
>
>
>
|
|
>
|


|

>
>
>
>
>
|
|
>
|
|







338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
static void win32_http_request(void *pAppData){
  HttpRequest *p = (HttpRequest*)pAppData;
  FILE *in = 0, *out = 0, *aux = 0;
  int amt, got, i;
  int wanted = 0;
  char *z;
  char *zIp;
  void *sslConn = 0;
  char zCmdFName[MAX_PATH];
  char zRequestFName[MAX_PATH];
  char zReplyFName[MAX_PATH];
  char zCmd[2000];          /* Command-line to process the request */
  char zBuf[65536];         /* The HTTP request header */
  const int szHdr = 4000;   /* Reduced header size */

  sqlite3_snprintf(MAX_PATH, zCmdFName,
                   "%s_%06d_cmd.txt", zTempPrefix, p->id);
  sqlite3_snprintf(MAX_PATH, zRequestFName,
                   "%s_%06d_in.txt", zTempPrefix, p->id);
  sqlite3_snprintf(MAX_PATH, zReplyFName,
                   "%s_%06d_out.txt", zTempPrefix, p->id);
  amt = 0;
  if( g.httpUseSSL ){
#ifdef FOSSIL_ENABLE_SSL
    sslConn = ssl_new_server(p->s);
#endif
  }
  while( amt<szHdr ){
    if( sslConn ){
#ifdef FOSSIL_ENABLE_SSL
      got = ssl_read_server(sslConn, &zBuf[amt], szHdr-amt);
#endif
    }else{
      got = recv(p->s, &zBuf[amt], szHdr-amt, 0);
      if( got==SOCKET_ERROR ) goto end_request;
    }
    if( got==0 ){
      wanted = 0;
      break;
    }
    amt += got;
    zBuf[amt] = 0;
    z = strstr(zBuf, "\r\n\r\n");
    if( z ){
      wanted = find_content_length(zBuf) + (&z[4]-zBuf) - amt;
      break;
    }else{
      z = strstr(zBuf, "\n\n");
      if( z ){
        wanted = find_content_length(zBuf) + (&z[2]-zBuf) - amt;
        break;
      }
    }
  }
  if( amt>=szHdr ) goto end_request;
  out = fossil_fopen(zRequestFName, "wb");
  if( out==0 ) goto end_request;
  fwrite(zBuf, 1, amt, out);
  while( wanted>0 ){
    if( sslConn ){
#ifdef FOSSIL_ENABLE_SSL
      got = ssl_read_server(sslConn, zBuf, sizeof(zBuf));
#endif
    }else{
      got = recv(p->s, zBuf, sizeof(zBuf), 0);
      if( got==SOCKET_ERROR ) goto end_request;
    }
    if( got>0 ){
      fwrite(zBuf, 1, got, out);
    }else{
      break;
    }
    wanted -= got;
  }

  /*
403
404
405
406
407
408
409
410
411

412
413
414
415
416
417
418





419

420
421
422
423
424
425
426
427





428
429
430
431
432
433
434
  }
  fossil_free(zIp);
  aux = fossil_fopen(zCmdFName, "wb");
  if( aux==0 ) goto end_request;
  fwrite(zCmd, 1, strlen(zCmd), aux);

  sqlite3_snprintf(sizeof(zCmd), zCmd,
    "\"%s\" http -args \"%s\" --nossl%s",
    g.nameOfExe, zCmdFName, p->zOptions

  );
  in = fossil_fopen(zReplyFName, "w+b");
  fflush(out);
  fflush(aux);
  fossil_system(zCmd);
  if( in ){
    while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){





      send(p->s, zHdr, got, 0);

    }
  }

end_request:
  if( out ) fclose(out);
  if( aux ) fclose(aux);
  if( in ) fclose(in);
  /* Initiate shutdown prior to closing the socket */





  if( shutdown(p->s,1)==0 ) shutdown(p->s,0);
  closesocket(p->s);
  /* Make multiple attempts to delete the temporary files.  Sometimes AV
  ** software keeps the files open for a few seconds, preventing the file
  ** from being deleted on the first try. */
  for(i=1; i<=10 && file_delete(zRequestFName); i++){ Sleep(1000*i); }
  for(i=1; i<=10 && file_delete(zCmdFName); i++){ Sleep(1000*i); }







|
|
>






|
>
>
>
>
>
|
>








>
>
>
>
>







428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
  }
  fossil_free(zIp);
  aux = fossil_fopen(zCmdFName, "wb");
  if( aux==0 ) goto end_request;
  fwrite(zCmd, 1, strlen(zCmd), aux);

  sqlite3_snprintf(sizeof(zCmd), zCmd,
    "\"%s\" http -args \"%s\"%s%s",
    g.nameOfExe, zCmdFName,
    g.httpUseSSL ? "" : " --nossl", p->zOptions
  );
  in = fossil_fopen(zReplyFName, "w+b");
  fflush(out);
  fflush(aux);
  fossil_system(zCmd);
  if( in ){
    while( (got = fread(zBuf, 1, sizeof(zBuf), in))>0 ){
      if( sslConn ){
#ifdef FOSSIL_ENABLE_SSL
        ssl_write_server(sslConn, zBuf, got);
#endif
      }else{
        send(p->s, zBuf, got, 0);
      }
    }
  }

end_request:
  if( out ) fclose(out);
  if( aux ) fclose(aux);
  if( in ) fclose(in);
  /* Initiate shutdown prior to closing the socket */
  if( sslConn!=0 ){
#ifdef FOSSIL_ENABLE_SSL
    ssl_close_server(sslConn);
#endif
  }
  if( shutdown(p->s,1)==0 ) shutdown(p->s,0);
  closesocket(p->s);
  /* Make multiple attempts to delete the temporary files.  Sometimes AV
  ** software keeps the files open for a few seconds, preventing the file
  ** from being deleted on the first try. */
  for(i=1; i<=10 && file_delete(zRequestFName); i++){ Sleep(1000*i); }
  for(i=1; i<=10 && file_delete(zCmdFName); i++){ Sleep(1000*i); }
617
618
619
620
621
622
623
624

625
626
627
628
629
630
631
  if( !GetTempPathW(MAX_PATH, zTmpPath) ){
    fossil_panic("unable to get path to the temporary directory.");
  }
  zTempPrefix = mprintf("%sfossil_server_P%d",
                        fossil_unicode_to_utf8(zTmpPath), iPort);
  fossil_print("Temporary files: %s*\n", zTempPrefix);
  fossil_print("Listening for %s requests on TCP port %d\n",
               (flags&HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort);

  if( zBrowser ){
    zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort);
    fossil_print("Launch webbrowser: %s\n", zBrowser);
    fossil_system(zBrowser);
  }
  fossil_print("Type Ctrl-C to stop the HTTP server\n");
  /* Create an event used to signal when this server is exiting. */







|
>







654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
  if( !GetTempPathW(MAX_PATH, zTmpPath) ){
    fossil_panic("unable to get path to the temporary directory.");
  }
  zTempPrefix = mprintf("%sfossil_server_P%d",
                        fossil_unicode_to_utf8(zTmpPath), iPort);
  fossil_print("Temporary files: %s*\n", zTempPrefix);
  fossil_print("Listening for %s requests on TCP port %d\n",
               (flags&HTTP_SERVER_SCGI)!=0 ? "SCGI" :
               g.httpUseSSL ? "TLS-encrypted HTTPS" : "HTTP", iPort);
  if( zBrowser ){
    zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort);
    fossil_print("Launch webbrowser: %s\n", zBrowser);
    fossil_system(zBrowser);
  }
  fossil_print("Type Ctrl-C to stop the HTTP server\n");
  /* Create an event used to signal when this server is exiting. */