Fossil

Check-in [ebe1faabbc]
Login

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

Overview
Comment:Code cleanup. Fix the "cert" command so that it compiles even if FOSSIL_ENABLE_SSL is not used.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | jan-clientcert
Files: files | file ages | folders
SHA1: ebe1faabbc586cecb40cd222d86744ae3aac6faa
User & Date: drh 2011-03-30 21:00:58.228
Context
2011-03-31
15:30
Some rephrasing and code cleanup. check-in: cff102fe85 user: jan tags: jan-clientcert
2011-03-30
21:00
Code cleanup. Fix the "cert" command so that it compiles even if FOSSIL_ENABLE_SSL is not used. check-in: ebe1faabbc user: drh tags: jan-clientcert
20:58
Fix two potential SQL injection attacks. check-in: 71384ce668 user: drh tags: jan-clientcert
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/http_ssl.c.
27
28
29
30
31
32
33
34
35
36
37
38
39
40



41

42
43
















44

45
46
47
48
49
50
51
** at a time.  State information is stored in static variables.  The identity
** of the server is held in global variables that are set by url_parse().
**
** SSL support is abstracted out into this module because Fossil can
** be compiled without SSL support (which requires OpenSSL library)
*/

#include "config.h"

#ifdef FOSSIL_ENABLE_SSL

#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>





#include "http_ssl.h"
#include <assert.h>
















#include <sys/types.h>


/*
** There can only be a single OpenSSL IO connection open at a time.
** State information about that IO is stored in the following
** local variables:
*/
static int sslIsInit = 0;    /* True after global initialization */







<


<



>
>
>

>

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







27
28
29
30
31
32
33

34
35

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
** at a time.  State information is stored in static variables.  The identity
** of the server is held in global variables that are set by url_parse().
**
** SSL support is abstracted out into this module because Fossil can
** be compiled without SSL support (which requires OpenSSL library)
*/



#ifdef FOSSIL_ENABLE_SSL

#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <assert.h>
#include <sys/types.h>
#endif

#include "config.h"
#include "http_ssl.h"

/*
** Make sure the CERT table exists in the ~/.fossil database.
**
** This routine must be called in between two calls to db_swap_databases().
*/
static void create_cert_table_if_not_exist(void){
  static const char zSql[] = 
     @ CREATE TABLE IF NOT EXISTS certs(
     @   name TEXT NOT NULL,
     @   type TEXT NOT NULL,
     @   filepath TEXT NOT NULL,
     @   PRIMARY KEY(name, type)
     @ );
     ;
  db_multi_exec(zSql);    
}

#ifdef FOSSIL_ENABLE_SSL

/*
** There can only be a single OpenSSL IO connection open at a time.
** State information about that IO is stored in the following
** local variables:
*/
static int sslIsInit = 0;    /* True after global initialization */
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
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
    total += got;
    N -= got;
    pContent = (void*)&((char*)pContent)[got];
  }
  return total;
}

#if 0
/*
** Read client certificate and key, if set, and store them in the SSL context
** to allow communication with servers which are configured to verify client
** certificates and certificate chains.
** We only support PEM and don't support password protected keys.
**
** Always try the environment variables first, and if they aren't set, then
** use the global config.
*/
void ssl_load_client_authfiles(void){
  char *cafile;
  char *capath;
  char *certfile;
  char *keyfile;

  cafile = ssl_get_and_set_file_ref("FOSSIL_CAFILE", "cafile");
  capath = ssl_get_and_set_file_ref("FOSSIL_CAPATH", "capath");

  if( cafile || capath ){
    /* The OpenSSL documentation warns that if several CA certificates match
    ** the same name, key identifier and serial number conditions, only the
    ** first will be examined. The caveat situation is when one stores an
    ** expired CA certificate among the valid ones.
    ** Simply put: Do not mix expired and valid certificates.
    */
    if( SSL_CTX_load_verify_locations(sslCtx, cafile, capath) == 0){
      fossil_fatal("SSL: Unable to load CA verification file/path");
    }
  }else{
    fossil_warning("SSL: CA file/path missing for certificate verification.");
  }

  certfile = ssl_get_and_set_file_ref("FOSSIL_CCERT", "ccert");
  if( !certfile ){
     free(capath);
     free(cafile);
     return;
  }

  keyfile = ssl_get_and_set_file_ref("FOSSIL_CKEY", "ckey");

  /* Assume the key is in the certificate file if key file was not specified */
  if( certfile && !keyfile ){
    keyfile = certfile;
  }

  if( SSL_CTX_use_certificate_file(sslCtx, certfile, SSL_FILETYPE_PEM) <= 0 ){
    fossil_fatal("SSL: Unable to open client certificate in %s.", certfile);
  }
  if( SSL_CTX_use_PrivateKey_file(sslCtx, keyfile, SSL_FILETYPE_PEM) <= 0 ){
    fossil_fatal("SSL: Unable to open client key in %s.", keyfile);
  }

  if( !SSL_CTX_check_private_key(sslCtx) ){
    fossil_fatal("SSL: Private key does not match the certificate public "
        "key.");
  }

  free(keyfile);
  free(certfile);
  free(capath);
  free(cafile);
}
#endif

/*
** If an certgroup has been specified on the command line, then use it to look
** up certificates and keys, and then store the URL-certgroup association in
** the global database. If no certgroup has been specified on the command line,
** see if there's an entry for the url in global_config, and use it if
** applicable.
*/







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







306
307
308
309
310
311
312


































































313
314
315
316
317
318
319
    total += got;
    N -= got;
    pContent = (void*)&((char*)pContent)[got];
  }
  return total;
}



































































/*
** If an certgroup has been specified on the command line, then use it to look
** up certificates and keys, and then store the URL-certgroup association in
** the global database. If no certgroup has been specified on the command line,
** see if there's an entry for the url in global_config, and use it if
** applicable.
*/
385
386
387
388
389
390
391

392
393
394
395
396
397
398
  }
  if( !zGroupName ){
    /* No cert group specified or found cached */
    return;
  }

  db_swap_connections();

  cafile = db_text(0, "SELECT filepath FROM certs WHERE name=%Q"
                      " AND type='cafile'", zGroupName);
  capath = db_text(0, "SELECT filepath FROM certs WHERE name=%Q"
                      " AND type='capath'", zGroupName);
  db_swap_connections();

  if( cafile || capath ){







>







338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
  }
  if( !zGroupName ){
    /* No cert group specified or found cached */
    return;
  }

  db_swap_connections();
  create_cert_table_if_not_exist();
  cafile = db_text(0, "SELECT filepath FROM certs WHERE name=%Q"
                      " AND type='cafile'", zGroupName);
  capath = db_text(0, "SELECT filepath FROM certs WHERE name=%Q"
                      " AND type='capath'", zGroupName);
  db_swap_connections();

  if( cafile || capath ){
427
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
  }

  free(keyfile);
  free(certfile);
  free(capath);
  free(cafile);
}

#if 0
/*
** Get SSL authentication file reference from environment variable. If set,
** then store varaible in global config. If environment variable was not set,
** attempt to get variable from global config.
**/
char *ssl_get_and_set_file_ref(const char *envvar, const char *dbvar){
  char *zVar;
  char *zTmp;

  zTmp = mprintf("%s:%s", dbvar, g.urlName);

  zVar = getenv(envvar);
  if( zVar ){
    zVar = strdup(zVar);
    if( zVar == NULL ){
      fossil_fatal("Unable to allocate memory for %s value.", envvar);
    }
    db_set(zTmp, zVar, 1);
  }else{
    zVar = db_get(zTmp, NULL);
  }
  free(zTmp);

  return zVar;
}
#endif

/*
** COMMAND: cert
**
** Usage: %fossil cert SUBCOMMAND ...
**
** Manage/group PKI keys/certificates to be able to use client







|
<
<
<
<
<
<
<
<
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







381
382
383
384
385
386
387
388









389

















390
391
392
393
394
395
396
  }

  free(keyfile);
  free(certfile);
  free(capath);
  free(cafile);
}
#endif /* FOSSIL_ENABLE_SSL */




























/*
** COMMAND: cert
**
** Usage: %fossil cert SUBCOMMAND ...
**
** Manage/group PKI keys/certificates to be able to use client
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
     */
    if( !zCKey && zCCert ){
      zCKey = zCCert;
    }

    db_open_config(0);
    db_swap_connections();
    if( db_exists(
        "SELECT 1 FROM certs"
        " WHERE name='%q'",
        zContainer)!=0 ){
      fossil_fatal("certificate group \"%s\" already exists", zContainer);
    }
    db_begin_transaction();
    if( zCKey ){
      db_multi_exec("INSERT INTO certs (name,type,filepath) "
          "VALUES(%Q,'ckey',%Q)",
          zContainer, zCKey);
    }
    if( zCCert ){
      db_multi_exec("INSERT INTO certs (name,type,filepath) "







|
|
|
|


<







447
448
449
450
451
452
453
454
455
456
457
458
459

460
461
462
463
464
465
466
     */
    if( !zCKey && zCCert ){
      zCKey = zCCert;
    }

    db_open_config(0);
    db_swap_connections();
    create_cert_table_if_not_exist();
    db_begin_transaction();
    if( db_exists("SELECT 1 FROM certs WHERE name='%q'", zContainer)!=0 ){
      db_end_transaction(0);
      fossil_fatal("certificate group \"%s\" already exists", zContainer);
    }

    if( zCKey ){
      db_multi_exec("INSERT INTO certs (name,type,filepath) "
          "VALUES(%Q,'ckey',%Q)",
          zContainer, zCKey);
    }
    if( zCCert ){
      db_multi_exec("INSERT INTO certs (name,type,filepath) "
554
555
556
557
558
559
560

561
562
563
564
565
566
567
    db_swap_connections();
  }else if(strncmp(zCmd, "list", n)==0){
    Stmt q;
    char *grp = NULL;

    db_open_config(0);
    db_swap_connections();


    db_prepare(&q, "SELECT name,type,filepath FROM certs"
                   " WHERE type NOT IN ('server')"
                   " ORDER BY name,type");
    while( db_step(&q)==SQLITE_ROW ){
      const char *zCont = db_column_text(&q, 0);
      const char *zType = db_column_text(&q, 1);







>







481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
    db_swap_connections();
  }else if(strncmp(zCmd, "list", n)==0){
    Stmt q;
    char *grp = NULL;

    db_open_config(0);
    db_swap_connections();
    create_cert_table_if_not_exist();

    db_prepare(&q, "SELECT name,type,filepath FROM certs"
                   " WHERE type NOT IN ('server')"
                   " ORDER BY name,type");
    while( db_step(&q)==SQLITE_ROW ){
      const char *zCont = db_column_text(&q, 0);
      const char *zType = db_column_text(&q, 1);
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
      usage("disassociate URL");
    }
    zURL = g.argv[3];

    db_open_config(0);
    db_swap_connections();
    db_begin_transaction();

    db_multi_exec("DELETE FROM global_config WHERE name='certgroup:%q'",
        zURL);
    if( db_changes() == 0 ){
      fossil_warning("No certificate group associated with URL \"%s\".",
          zURL);
    }else{
      printf("%s disassociated from its certificate group.\n", zURL);







<







526
527
528
529
530
531
532

533
534
535
536
537
538
539
      usage("disassociate URL");
    }
    zURL = g.argv[3];

    db_open_config(0);
    db_swap_connections();
    db_begin_transaction();

    db_multi_exec("DELETE FROM global_config WHERE name='certgroup:%q'",
        zURL);
    if( db_changes() == 0 ){
      fossil_warning("No certificate group associated with URL \"%s\".",
          zURL);
    }else{
      printf("%s disassociated from its certificate group.\n", zURL);
639
640
641
642
643
644
645
646
647
    db_end_transaction(0);
    db_swap_connections();
  }else{
    fossil_panic("cert subcommand should be one of: "
                 "add list disassociate delete");
  }
}

#endif /* FOSSIL_ENABLE_SSL */







<
<
566
567
568
569
570
571
572


    db_end_transaction(0);
    db_swap_connections();
  }else{
    fossil_panic("cert subcommand should be one of: "
                 "add list disassociate delete");
  }
}


Changes to src/schema.c.
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@ -- This file contains the schema for the database that is kept in the
@ -- ~/.fossil file and that stores information about the users setup.
@ --
@ CREATE TABLE global_config(
@   name TEXT PRIMARY KEY,
@   value TEXT
@ );
@ CREATE TABLE certs(
@   name TEXT NOT NULL,
@   type TEXT NOT NULL,
@   filepath TEXT NOT NULL,
@   PRIMARY KEY(name, type),
@   UNIQUE(name, type)
@ );
;

#if INTERFACE
/*
** The content tables have a content version number which rarely
** changes.  The aux tables have an arbitrary version number (typically
** a date) which can change frequently.  When the content schema changes,







<
<
<
<
<
<
<







27
28
29
30
31
32
33







34
35
36
37
38
39
40
@ -- This file contains the schema for the database that is kept in the
@ -- ~/.fossil file and that stores information about the users setup.
@ --
@ CREATE TABLE global_config(
@   name TEXT PRIMARY KEY,
@   value TEXT
@ );







;

#if INTERFACE
/*
** The content tables have a content version number which rarely
** changes.  The aux tables have an arbitrary version number (typically
** a date) which can change frequently.  When the content schema changes,