Fossil SCM
Added a 'cert' subcommand to manage certificate groups, and added a certificate table to the global db. Minor code formatting change.
Commit
1156ad25dbb67cefd5c8c5bb244edb3e620ad551
Parent
662c83513fcc846…
2 files changed
+184
-2
+7
+184
-2
| --- src/http_ssl.c | ||
| +++ src/http_ssl.c | ||
| @@ -358,12 +358,11 @@ | ||
| 358 | 358 | /* |
| 359 | 359 | ** Get SSL authentication file reference from environment variable. If set, |
| 360 | 360 | ** then store varaible in global config. If environment variable was not set, |
| 361 | 361 | ** attempt to get variable from global config. |
| 362 | 362 | **/ |
| 363 | -char *ssl_get_and_set_file_ref(const char *envvar, const char *dbvar) | |
| 364 | -{ | |
| 363 | +char *ssl_get_and_set_file_ref(const char *envvar, const char *dbvar){ | |
| 365 | 364 | char *zVar; |
| 366 | 365 | char *zTmp; |
| 367 | 366 | |
| 368 | 367 | zTmp = mprintf("%s:%s", dbvar, g.urlName); |
| 369 | 368 | |
| @@ -379,7 +378,190 @@ | ||
| 379 | 378 | } |
| 380 | 379 | free(zTmp); |
| 381 | 380 | |
| 382 | 381 | return zVar; |
| 383 | 382 | } |
| 383 | + | |
| 384 | +/* | |
| 385 | +** COMMAND: cert | |
| 386 | +** | |
| 387 | +** Usage: %fossil cert SUBCOMMAND ... | |
| 388 | +** | |
| 389 | +** Manage/group PKI keys/certificates to be able to use client | |
| 390 | +** certificates and register CA certificates for SSL verifications. | |
| 391 | +** | |
| 392 | +** %fossil cert add NAME ?--key KEYFILE? ?--cert CERTFILE? | |
| 393 | +** ?--cafile CAFILE? ?--capath CAPATH? | |
| 394 | +** | |
| 395 | +** Create a certificate group NAME with the associated | |
| 396 | +** certificates/keys. If a client certificate is specified but no | |
| 397 | +** key, it is assumed that the key is located in the client | |
| 398 | +** certificate file. The file format must be PEM. | |
| 399 | +** | |
| 400 | +** %fossil cert list | |
| 401 | +** | |
| 402 | +** List all credential groups, their values and their URL | |
| 403 | +** associations. | |
| 404 | +** | |
| 405 | +** %fossil cert disassociate URL | |
| 406 | +** | |
| 407 | +** Disassociate URL from any credential group(s). | |
| 408 | +** | |
| 409 | +** %fossil cert delete NAME | |
| 410 | +** | |
| 411 | +** Remove the credential group NAME and all it's associated URL | |
| 412 | +** associations. | |
| 413 | +*/ | |
| 414 | +void cert_cmd(void){ | |
| 415 | + int n; | |
| 416 | + const char *zCmd = "list"; | |
| 417 | + if( g.argc>=3 ){ | |
| 418 | + zCmd = g.argv[2]; | |
| 419 | + } | |
| 420 | + n = strlen(zCmd); | |
| 421 | + if( strncmp(zCmd, "add", n)==0 ){ | |
| 422 | + const char *zContainer; | |
| 423 | + const char *zCKey; | |
| 424 | + const char *zCCert; | |
| 425 | + const char *zCAFile; | |
| 426 | + const char *zCAPath; | |
| 427 | + if( g.argc<5 ){ | |
| 428 | + usage("add NAME ?--key CLIENTKEY? ?--cert CLIENTCERT? ?--cafile CAFILE? " | |
| 429 | + "?--capath CAPATH?"); | |
| 430 | + } | |
| 431 | + zContainer = g.argv[3]; | |
| 432 | + zCKey = find_option("key",0,1); | |
| 433 | + zCCert = find_option("cert",0,1); | |
| 434 | + zCAFile = find_option("cafile",0,1); | |
| 435 | + zCAPath = find_option("capath",0,1); | |
| 436 | + | |
| 437 | + /* If a client certificate was specified, but a key was not, assume the | |
| 438 | + * key is stored in the same file as the certificate. | |
| 439 | + */ | |
| 440 | + if( !zCKey && zCCert ){ | |
| 441 | + zCKey = zCCert; | |
| 442 | + } | |
| 443 | + | |
| 444 | + db_open_config(0); | |
| 445 | + db_swap_connections(); | |
| 446 | + if( db_exists( | |
| 447 | + "SELECT 1 FROM certs" | |
| 448 | + " WHERE name='%s'", | |
| 449 | + zContainer)!=0 ){ | |
| 450 | + fossil_fatal("certificate group \"%s\" already exists", zContainer); | |
| 451 | + } | |
| 452 | + db_begin_transaction(); | |
| 453 | + if( zCKey ){ | |
| 454 | + db_multi_exec("INSERT INTO certs (name,type,filepath) " | |
| 455 | + "VALUES(%Q,'ckey',%Q)", | |
| 456 | + zContainer, zCKey); | |
| 457 | + } | |
| 458 | + if( zCCert ){ | |
| 459 | + db_multi_exec("INSERT INTO certs (name,type,filepath) " | |
| 460 | + "VALUES(%Q,'ccert',%Q)", | |
| 461 | + zContainer, zCCert); | |
| 462 | + } | |
| 463 | + if( zCAFile ){ | |
| 464 | + db_multi_exec("INSERT INTO certs (name,type,filepath) " | |
| 465 | + "VALUES(%Q,'cafile',%Q)", | |
| 466 | + zContainer, zCAFile); | |
| 467 | + } | |
| 468 | + if( zCAPath ){ | |
| 469 | + db_multi_exec("INSERT INTO certs (name,type,filepath) " | |
| 470 | + "VALUES(%Q,'capath',%Q)", | |
| 471 | + zContainer, zCAPath); | |
| 472 | + } | |
| 473 | + db_end_transaction(0); | |
| 474 | + db_swap_connections(); | |
| 475 | + }else if(strncmp(zCmd, "list", n)==0){ | |
| 476 | + Stmt q; | |
| 477 | + char *grp = NULL; | |
| 478 | + | |
| 479 | + db_open_config(0); | |
| 480 | + db_swap_connections(); | |
| 481 | + | |
| 482 | + db_prepare(&q, "SELECT name,type,filepath FROM certs" | |
| 483 | + " WHERE type NOT IN ('server')" | |
| 484 | + " ORDER BY name,type"); | |
| 485 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 486 | + const char *zCont = db_column_text(&q, 0); | |
| 487 | + const char *zType = db_column_text(&q, 1); | |
| 488 | + const char *zFilePath = db_column_text(&q, 2); | |
| 489 | + if( fossil_strcmp(zCont, grp)!=0 ){ | |
| 490 | + free(grp); | |
| 491 | + grp = strdup(zCont); | |
| 492 | + puts(zCont); | |
| 493 | + } | |
| 494 | + printf("\t%s=%s\n", zType, zFilePath); | |
| 495 | + } | |
| 496 | + db_finalize(&q); | |
| 497 | + | |
| 498 | + /* List the URL associations. */ | |
| 499 | + db_prepare(&q, "SELECT name FROM global_config" | |
| 500 | + " WHERE name LIKE 'certgroup:%%' AND value=%Q" | |
| 501 | + " ORDER BY name", grp); | |
| 502 | + free(grp); | |
| 503 | + | |
| 504 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 505 | + const char *zName = db_column_text(&q, 0); | |
| 506 | + static int first = 1; | |
| 507 | + if( first ) { | |
| 508 | + puts("\tAssociations"); | |
| 509 | + first = 0; | |
| 510 | + } | |
| 511 | + printf("\t\t%s\n", zName+10); | |
| 512 | + } | |
| 513 | + | |
| 514 | + db_swap_connections(); | |
| 515 | + }else if(strncmp(zCmd, "disassociate", n)==0){ | |
| 516 | + const char *zURL; | |
| 517 | + if( g.argc<4 ){ | |
| 518 | + usage("disassociate URL"); | |
| 519 | + } | |
| 520 | + zURL = g.argv[3]; | |
| 521 | + | |
| 522 | + db_open_config(0); | |
| 523 | + db_swap_connections(); | |
| 524 | + db_begin_transaction(); | |
| 525 | + | |
| 526 | + db_multi_exec("DELETE FROM global_config WHERE name='certgroup:%s'", | |
| 527 | + zURL); | |
| 528 | + if( db_changes() == 0 ){ | |
| 529 | + fossil_warning("No certificate group associated with URL \"%s\".", | |
| 530 | + zURL); | |
| 531 | + }else{ | |
| 532 | + printf("%s disassociated from its certificate group.\n", zURL); | |
| 533 | + } | |
| 534 | + db_end_transaction(0); | |
| 535 | + db_swap_connections(); | |
| 536 | + | |
| 537 | + }else if(strncmp(zCmd, "delete", n)==0){ | |
| 538 | + const char *zContainer; | |
| 539 | + if( g.argc<4 ){ | |
| 540 | + usage("delete NAME"); | |
| 541 | + } | |
| 542 | + zContainer = g.argv[3]; | |
| 543 | + | |
| 544 | + db_open_config(0); | |
| 545 | + db_swap_connections(); | |
| 546 | + db_begin_transaction(); | |
| 547 | + db_multi_exec("DELETE FROM certs WHERE name=%Q", zContainer); | |
| 548 | + if( db_changes() == 0 ){ | |
| 549 | + fossil_warning("No certificate group named \"%s\" found", | |
| 550 | + zContainer); | |
| 551 | + }else{ | |
| 552 | + printf("%d entries removed\n", db_changes()); | |
| 553 | + } | |
| 554 | + db_multi_exec("DELETE FROM global_config WHERE name LIKE 'certgroup:%%'" | |
| 555 | + " AND value=%Q", zContainer); | |
| 556 | + if( db_changes() > 0 ){ | |
| 557 | + printf("%d associations removed\n", db_changes()); | |
| 558 | + } | |
| 559 | + db_end_transaction(0); | |
| 560 | + db_swap_connections(); | |
| 561 | + }else{ | |
| 562 | + fossil_panic("cert subcommand should be one of: " | |
| 563 | + "add list disassociate delete"); | |
| 564 | + } | |
| 565 | +} | |
| 384 | 566 | |
| 385 | 567 | #endif /* FOSSIL_ENABLE_SSL */ |
| 386 | 568 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -358,12 +358,11 @@ | |
| 358 | /* |
| 359 | ** Get SSL authentication file reference from environment variable. If set, |
| 360 | ** then store varaible in global config. If environment variable was not set, |
| 361 | ** attempt to get variable from global config. |
| 362 | **/ |
| 363 | char *ssl_get_and_set_file_ref(const char *envvar, const char *dbvar) |
| 364 | { |
| 365 | char *zVar; |
| 366 | char *zTmp; |
| 367 | |
| 368 | zTmp = mprintf("%s:%s", dbvar, g.urlName); |
| 369 | |
| @@ -379,7 +378,190 @@ | |
| 379 | } |
| 380 | free(zTmp); |
| 381 | |
| 382 | return zVar; |
| 383 | } |
| 384 | |
| 385 | #endif /* FOSSIL_ENABLE_SSL */ |
| 386 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -358,12 +358,11 @@ | |
| 358 | /* |
| 359 | ** Get SSL authentication file reference from environment variable. If set, |
| 360 | ** then store varaible in global config. If environment variable was not set, |
| 361 | ** attempt to get variable from global config. |
| 362 | **/ |
| 363 | char *ssl_get_and_set_file_ref(const char *envvar, const char *dbvar){ |
| 364 | char *zVar; |
| 365 | char *zTmp; |
| 366 | |
| 367 | zTmp = mprintf("%s:%s", dbvar, g.urlName); |
| 368 | |
| @@ -379,7 +378,190 @@ | |
| 378 | } |
| 379 | free(zTmp); |
| 380 | |
| 381 | return zVar; |
| 382 | } |
| 383 | |
| 384 | /* |
| 385 | ** COMMAND: cert |
| 386 | ** |
| 387 | ** Usage: %fossil cert SUBCOMMAND ... |
| 388 | ** |
| 389 | ** Manage/group PKI keys/certificates to be able to use client |
| 390 | ** certificates and register CA certificates for SSL verifications. |
| 391 | ** |
| 392 | ** %fossil cert add NAME ?--key KEYFILE? ?--cert CERTFILE? |
| 393 | ** ?--cafile CAFILE? ?--capath CAPATH? |
| 394 | ** |
| 395 | ** Create a certificate group NAME with the associated |
| 396 | ** certificates/keys. If a client certificate is specified but no |
| 397 | ** key, it is assumed that the key is located in the client |
| 398 | ** certificate file. The file format must be PEM. |
| 399 | ** |
| 400 | ** %fossil cert list |
| 401 | ** |
| 402 | ** List all credential groups, their values and their URL |
| 403 | ** associations. |
| 404 | ** |
| 405 | ** %fossil cert disassociate URL |
| 406 | ** |
| 407 | ** Disassociate URL from any credential group(s). |
| 408 | ** |
| 409 | ** %fossil cert delete NAME |
| 410 | ** |
| 411 | ** Remove the credential group NAME and all it's associated URL |
| 412 | ** associations. |
| 413 | */ |
| 414 | void cert_cmd(void){ |
| 415 | int n; |
| 416 | const char *zCmd = "list"; |
| 417 | if( g.argc>=3 ){ |
| 418 | zCmd = g.argv[2]; |
| 419 | } |
| 420 | n = strlen(zCmd); |
| 421 | if( strncmp(zCmd, "add", n)==0 ){ |
| 422 | const char *zContainer; |
| 423 | const char *zCKey; |
| 424 | const char *zCCert; |
| 425 | const char *zCAFile; |
| 426 | const char *zCAPath; |
| 427 | if( g.argc<5 ){ |
| 428 | usage("add NAME ?--key CLIENTKEY? ?--cert CLIENTCERT? ?--cafile CAFILE? " |
| 429 | "?--capath CAPATH?"); |
| 430 | } |
| 431 | zContainer = g.argv[3]; |
| 432 | zCKey = find_option("key",0,1); |
| 433 | zCCert = find_option("cert",0,1); |
| 434 | zCAFile = find_option("cafile",0,1); |
| 435 | zCAPath = find_option("capath",0,1); |
| 436 | |
| 437 | /* If a client certificate was specified, but a key was not, assume the |
| 438 | * key is stored in the same file as the certificate. |
| 439 | */ |
| 440 | if( !zCKey && zCCert ){ |
| 441 | zCKey = zCCert; |
| 442 | } |
| 443 | |
| 444 | db_open_config(0); |
| 445 | db_swap_connections(); |
| 446 | if( db_exists( |
| 447 | "SELECT 1 FROM certs" |
| 448 | " WHERE name='%s'", |
| 449 | zContainer)!=0 ){ |
| 450 | fossil_fatal("certificate group \"%s\" already exists", zContainer); |
| 451 | } |
| 452 | db_begin_transaction(); |
| 453 | if( zCKey ){ |
| 454 | db_multi_exec("INSERT INTO certs (name,type,filepath) " |
| 455 | "VALUES(%Q,'ckey',%Q)", |
| 456 | zContainer, zCKey); |
| 457 | } |
| 458 | if( zCCert ){ |
| 459 | db_multi_exec("INSERT INTO certs (name,type,filepath) " |
| 460 | "VALUES(%Q,'ccert',%Q)", |
| 461 | zContainer, zCCert); |
| 462 | } |
| 463 | if( zCAFile ){ |
| 464 | db_multi_exec("INSERT INTO certs (name,type,filepath) " |
| 465 | "VALUES(%Q,'cafile',%Q)", |
| 466 | zContainer, zCAFile); |
| 467 | } |
| 468 | if( zCAPath ){ |
| 469 | db_multi_exec("INSERT INTO certs (name,type,filepath) " |
| 470 | "VALUES(%Q,'capath',%Q)", |
| 471 | zContainer, zCAPath); |
| 472 | } |
| 473 | db_end_transaction(0); |
| 474 | db_swap_connections(); |
| 475 | }else if(strncmp(zCmd, "list", n)==0){ |
| 476 | Stmt q; |
| 477 | char *grp = NULL; |
| 478 | |
| 479 | db_open_config(0); |
| 480 | db_swap_connections(); |
| 481 | |
| 482 | db_prepare(&q, "SELECT name,type,filepath FROM certs" |
| 483 | " WHERE type NOT IN ('server')" |
| 484 | " ORDER BY name,type"); |
| 485 | while( db_step(&q)==SQLITE_ROW ){ |
| 486 | const char *zCont = db_column_text(&q, 0); |
| 487 | const char *zType = db_column_text(&q, 1); |
| 488 | const char *zFilePath = db_column_text(&q, 2); |
| 489 | if( fossil_strcmp(zCont, grp)!=0 ){ |
| 490 | free(grp); |
| 491 | grp = strdup(zCont); |
| 492 | puts(zCont); |
| 493 | } |
| 494 | printf("\t%s=%s\n", zType, zFilePath); |
| 495 | } |
| 496 | db_finalize(&q); |
| 497 | |
| 498 | /* List the URL associations. */ |
| 499 | db_prepare(&q, "SELECT name FROM global_config" |
| 500 | " WHERE name LIKE 'certgroup:%%' AND value=%Q" |
| 501 | " ORDER BY name", grp); |
| 502 | free(grp); |
| 503 | |
| 504 | while( db_step(&q)==SQLITE_ROW ){ |
| 505 | const char *zName = db_column_text(&q, 0); |
| 506 | static int first = 1; |
| 507 | if( first ) { |
| 508 | puts("\tAssociations"); |
| 509 | first = 0; |
| 510 | } |
| 511 | printf("\t\t%s\n", zName+10); |
| 512 | } |
| 513 | |
| 514 | db_swap_connections(); |
| 515 | }else if(strncmp(zCmd, "disassociate", n)==0){ |
| 516 | const char *zURL; |
| 517 | if( g.argc<4 ){ |
| 518 | usage("disassociate URL"); |
| 519 | } |
| 520 | zURL = g.argv[3]; |
| 521 | |
| 522 | db_open_config(0); |
| 523 | db_swap_connections(); |
| 524 | db_begin_transaction(); |
| 525 | |
| 526 | db_multi_exec("DELETE FROM global_config WHERE name='certgroup:%s'", |
| 527 | zURL); |
| 528 | if( db_changes() == 0 ){ |
| 529 | fossil_warning("No certificate group associated with URL \"%s\".", |
| 530 | zURL); |
| 531 | }else{ |
| 532 | printf("%s disassociated from its certificate group.\n", zURL); |
| 533 | } |
| 534 | db_end_transaction(0); |
| 535 | db_swap_connections(); |
| 536 | |
| 537 | }else if(strncmp(zCmd, "delete", n)==0){ |
| 538 | const char *zContainer; |
| 539 | if( g.argc<4 ){ |
| 540 | usage("delete NAME"); |
| 541 | } |
| 542 | zContainer = g.argv[3]; |
| 543 | |
| 544 | db_open_config(0); |
| 545 | db_swap_connections(); |
| 546 | db_begin_transaction(); |
| 547 | db_multi_exec("DELETE FROM certs WHERE name=%Q", zContainer); |
| 548 | if( db_changes() == 0 ){ |
| 549 | fossil_warning("No certificate group named \"%s\" found", |
| 550 | zContainer); |
| 551 | }else{ |
| 552 | printf("%d entries removed\n", db_changes()); |
| 553 | } |
| 554 | db_multi_exec("DELETE FROM global_config WHERE name LIKE 'certgroup:%%'" |
| 555 | " AND value=%Q", zContainer); |
| 556 | if( db_changes() > 0 ){ |
| 557 | printf("%d associations removed\n", db_changes()); |
| 558 | } |
| 559 | db_end_transaction(0); |
| 560 | db_swap_connections(); |
| 561 | }else{ |
| 562 | fossil_panic("cert subcommand should be one of: " |
| 563 | "add list disassociate delete"); |
| 564 | } |
| 565 | } |
| 566 | |
| 567 | #endif /* FOSSIL_ENABLE_SSL */ |
| 568 |
+7
| --- src/schema.c | ||
| +++ src/schema.c | ||
| @@ -29,10 +29,17 @@ | ||
| 29 | 29 | @ -- |
| 30 | 30 | @ CREATE TABLE global_config( |
| 31 | 31 | @ name TEXT PRIMARY KEY, |
| 32 | 32 | @ value TEXT |
| 33 | 33 | @ ); |
| 34 | +@ CREATE TABLE certs( | |
| 35 | +@ name TEXT NOT NULL, | |
| 36 | +@ type TEXT NOT NULL, | |
| 37 | +@ filepath TEXT NOT NULL, | |
| 38 | +@ PRIMARY KEY(name, type), | |
| 39 | +@ UNIQUE(name, type) | |
| 40 | +@ ); | |
| 34 | 41 | ; |
| 35 | 42 | |
| 36 | 43 | #if INTERFACE |
| 37 | 44 | /* |
| 38 | 45 | ** The content tables have a content version number which rarely |
| 39 | 46 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -29,10 +29,17 @@ | |
| 29 | @ -- |
| 30 | @ CREATE TABLE global_config( |
| 31 | @ name TEXT PRIMARY KEY, |
| 32 | @ value TEXT |
| 33 | @ ); |
| 34 | ; |
| 35 | |
| 36 | #if INTERFACE |
| 37 | /* |
| 38 | ** The content tables have a content version number which rarely |
| 39 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -29,10 +29,17 @@ | |
| 29 | @ -- |
| 30 | @ CREATE TABLE global_config( |
| 31 | @ name TEXT PRIMARY KEY, |
| 32 | @ value TEXT |
| 33 | @ ); |
| 34 | @ CREATE TABLE certs( |
| 35 | @ name TEXT NOT NULL, |
| 36 | @ type TEXT NOT NULL, |
| 37 | @ filepath TEXT NOT NULL, |
| 38 | @ PRIMARY KEY(name, type), |
| 39 | @ UNIQUE(name, type) |
| 40 | @ ); |
| 41 | ; |
| 42 | |
| 43 | #if INTERFACE |
| 44 | /* |
| 45 | ** The content tables have a content version number which rarely |
| 46 |