From: Landon Fuller Date: Sun, 4 Dec 2005 02:09:23 +0000 (+0000) Subject: Merge the Bacula Encryption branch to HEAD. X-Git-Tag: Release-1.38.3~47 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=2c10d832689907ccbbd02369a7a397c545145837;p=bacula%2Fbacula Merge the Bacula Encryption branch to HEAD. Approved by: Kern git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@2638 91ce42f0-d328-0410-95d8-f526ca767f89 --- diff --git a/bacula/autoconf/config.h.in b/bacula/autoconf/config.h.in index 89a173a392..c9eee56660 100644 --- a/bacula/autoconf/config.h.in +++ b/bacula/autoconf/config.h.in @@ -271,6 +271,12 @@ /* Set if Bacula conio support enabled */ #undef HAVE_CONIO +/* Define if encryption support should be enabled */ +#undef HAVE_CRYPTO + +/* Define if the SHA-2 family of digest algorithms is available */ +#undef HAVE_SHA2 + /* Define to 1 if you have the header file. */ #undef HAVE_CURSES_H diff --git a/bacula/autoconf/configure.in b/bacula/autoconf/configure.in index 1844cb79fe..c0cac2b548 100644 --- a/bacula/autoconf/configure.in +++ b/bacula/autoconf/configure.in @@ -193,6 +193,7 @@ support_conio=yes support_gnome=no support_wx_console=no support_tls=no +support_crypto=no gnome_version= wx_version= support_static_tools=no @@ -751,19 +752,34 @@ if test "x$with_openssl_directory" != "x"; then AC_TRY_LINK([ #include ], [ CRYPTO_set_id_callback(NULL); ], - [ support_tls="yes" ], + [ + support_tls="yes" + support_crypto="yes" + ], [ support_tls="no" ] ) + AC_TRY_LINK([ #include ], + [ EVP_sha512(); ], + [ ac_cv_openssl_sha2="yes" ], + [ ac_cv_openssl_sha2="no" ] + ) + LIBS="$saved_LIBS" CFLAGS="$saved_CFLAGS" if test "$support_tls" = "yes"; then AC_DEFINE(HAVE_OPENSSL, 1, [Define if OpenSSL library is available]) AC_DEFINE(HAVE_TLS, 1, [Define if TLS support should be enabled]) + AC_DEFINE(HAVE_CRYPTO, 1, [Define if encryption support should be enabled]) + fi + + if test "$ac_cv_openssl_sha2" = "yes"; then + AC_DEFINE(HAVE_SHA2, 1, [Define if the SHA-2 family of digest algorithms is available]) fi else support_tls="no" + support_crypto="no" OPENSSL_LIBS="" OPENSSL_INC="" fi @@ -2112,6 +2128,7 @@ Configuration on `date`: readline support: ${got_readline} ${PRTREADLINE_SRC} TCP Wrappers support: ${TCPW_MSG} ${WRAPLIBS} TLS support: ${support_tls} + Encryption support: ${support_crypto} ZLIB support: ${have_zlib} enable-smartalloc: ${support_smartalloc} enable-gnome: ${support_gnome} ${gnome_version} diff --git a/bacula/configure b/bacula/configure index 48c8e5a5bf..ff48c3ab66 100755 --- a/bacula/configure +++ b/bacula/configure @@ -12770,6 +12770,7 @@ support_conio=yes support_gnome=no support_wx_console=no support_tls=no +support_crypto=no gnome_version= wx_version= support_static_tools=no @@ -15880,13 +15881,64 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then - support_tls="yes" + + support_tls="yes" + support_crypto="yes" + else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 support_tls="no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + #include +int +main () +{ + EVP_sha512(); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_openssl_sha2="yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_openssl_sha2="no" + fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext @@ -15905,9 +15957,23 @@ cat >>confdefs.h <<\_ACEOF #define HAVE_TLS 1 _ACEOF + +cat >>confdefs.h <<\_ACEOF +#define HAVE_CRYPTO 1 +_ACEOF + + fi + + if test "$ac_cv_openssl_sha2" = "yes"; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SHA2 1 +_ACEOF + fi else support_tls="no" + support_crypto="no" OPENSSL_LIBS="" OPENSSL_INC="" fi @@ -31536,6 +31602,7 @@ Configuration on `date`: readline support: ${got_readline} ${PRTREADLINE_SRC} TCP Wrappers support: ${TCPW_MSG} ${WRAPLIBS} TLS support: ${support_tls} + Encryption support: ${support_crypto} ZLIB support: ${have_zlib} enable-smartalloc: ${support_smartalloc} enable-gnome: ${support_gnome} ${gnome_version} diff --git a/bacula/src/baconfig.h b/bacula/src/baconfig.h index 53a6ac1508..e1139b5c1b 100644 --- a/bacula/src/baconfig.h +++ b/bacula/src/baconfig.h @@ -139,12 +139,15 @@ * * STREAM_UNIX_ATTRIBUTES * STREAM_UNIX_ATTRIBUTES_EX - * STREAM_MD5_SIGNATURE - * STREAM_SHA1_SIGNATURE + * STREAM_MD5_DIGEST + * STREAM_SHA1_DIGEST + * STREAM_SHA256_DIGEST + * STREAM_SHA512_DIGEST */ +#define STREAM_NONE 0 /* Reserved Non-Stream */ #define STREAM_UNIX_ATTRIBUTES 1 /* Generic Unix attributes */ #define STREAM_FILE_DATA 2 /* Standard uncompressed data */ -#define STREAM_MD5_SIGNATURE 3 /* MD5 signature for the file */ +#define STREAM_MD5_DIGEST 3 /* MD5 digest for the file */ #define STREAM_GZIP_DATA 4 /* GZip compressed file data */ /* Extended Unix attributes with Win32 Extended data. Deprecated. */ #define STREAM_UNIX_ATTRIBUTES_EX 5 /* Extended Unix attr for Win32 EX */ @@ -152,7 +155,7 @@ #define STREAM_SPARSE_GZIP_DATA 7 #define STREAM_PROGRAM_NAMES 8 /* program names for program data */ #define STREAM_PROGRAM_DATA 9 /* Data needing program */ -#define STREAM_SHA1_SIGNATURE 10 /* SHA1 signature for the file */ +#define STREAM_SHA1_DIGEST 10 /* SHA1 digest for the file */ #define STREAM_WIN32_DATA 11 /* Win32 BackupRead data */ #define STREAM_WIN32_GZIP_DATA 12 /* Gzipped Win32 BackupRead data */ #define STREAM_MACOS_FORK_DATA 13 /* Mac resource fork */ @@ -161,6 +164,11 @@ #define STREAM_UNIX_ATTRIBUTES_ACCESS_ACL 15 /* Standard ACL attributes on UNIX */ #define STREAM_UNIX_ATTRIBUTES_DEFAULT_ACL 16 /* Default ACL attributes on UNIX */ /*** FIXME ***/ +#define STREAM_SHA256_DIGEST 17 /* SHA-256 digest for the file */ +#define STREAM_SHA512_DIGEST 18 /* SHA-512 digest for the file */ +#define STREAM_SIGNED_DIGEST 19 /* Signed File Digest, ASN.1 Encoded */ +#define STREAM_ENCRYPTED_FILE_DATA 20 /* Encrypted, uncompressed data */ +#define STREAM_ENCRYPTED_WIN32_DATA 21 /* Encrypted, uncompressed Win32 BackupRead data */ /* @@ -199,13 +207,6 @@ /* Definitions for upper part of type word (see above). */ #define AR_DATA_STREAM (1<<16) /* Data stream id present */ -/* - * Internal code for Signature types - */ -#define NO_SIG 0 -#define MD5_SIG 1 -#define SHA1_SIG 2 - /* * Tape label types -- stored in catalog */ diff --git a/bacula/src/bacula.h b/bacula/src/bacula.h index e820a93c0e..6225d44e11 100644 --- a/bacula/src/bacula.h +++ b/bacula/src/bacula.h @@ -113,6 +113,8 @@ #include #include #include +#include +#include #undef STORE #endif diff --git a/bacula/src/cats/bdb_update.c b/bacula/src/cats/bdb_update.c index bea9cc8bec..7b1f054f8d 100755 --- a/bacula/src/cats/bdb_update.c +++ b/bacula/src/cats/bdb_update.c @@ -197,7 +197,7 @@ int db_update_pool_record(JCR *jcr, B_DB *mdb, POOL_DBR *pr) return stat; } -int db_add_SIG_to_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, char *SIG, int type) +int db_add_digest_to_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, char *digest, int type) { return 1; } diff --git a/bacula/src/cats/cats.h b/bacula/src/cats/cats.h index 069d9257f7..00ea5a0974 100644 --- a/bacula/src/cats/cats.h +++ b/bacula/src/cats/cats.h @@ -614,8 +614,8 @@ struct ATTR_DBR { DBId_t PathId; DBId_t FilenameId; FileId_t FileId; - char *Sig; - int SigType; + char *Digest; + int DigestType; }; @@ -628,8 +628,8 @@ struct FILE_DBR { DBId_t PathId; JobId_t MarkId; char LStat[256]; - char SIG[50]; - int SigType; /* NO_SIG/MD5_SIG/SHA1_SIG */ + char Digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)]; + int DigestType; /* NO_SIG/MD5_SIG/SHA1_SIG */ }; /* Pool record -- same format as database */ diff --git a/bacula/src/cats/protos.h b/bacula/src/cats/protos.h index 24e5c2c8d5..580d3c387b 100644 --- a/bacula/src/cats/protos.h +++ b/bacula/src/cats/protos.h @@ -110,7 +110,7 @@ bool db_update_storage_record(JCR *jcr, B_DB *mdb, STORAGE_DBR *sr); int db_update_media_record(JCR *jcr, B_DB *db, MEDIA_DBR *mr); int db_update_media_defaults(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr); int db_update_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr); -int db_add_SIG_to_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, char *SIG, int type); +int db_add_digest_to_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, char *digest, int type); int db_mark_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t JobId); void db_make_inchanger_unique(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr); diff --git a/bacula/src/cats/sql_create.c b/bacula/src/cats/sql_create.c index fb46704122..8f06098156 100644 --- a/bacula/src/cats/sql_create.c +++ b/bacula/src/cats/sql_create.c @@ -709,17 +709,17 @@ bail_out: static int db_create_file_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar) { int stat; - static char *no_sig = "0"; - char *sig; + static char *no_digest = "0"; + char *digest; ASSERT(ar->JobId); ASSERT(ar->PathId); ASSERT(ar->FilenameId); - if (ar->Sig == NULL) { - sig = no_sig; + if (ar->Digest == NULL) { + digest = no_digest; } else { - sig = ar->Sig; + digest = ar->Digest; } /* Must create it */ @@ -727,7 +727,7 @@ static int db_create_file_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar) "INSERT INTO File (FileIndex,JobId,PathId,FilenameId," "LStat,MD5) VALUES (%u,%u,%u,%u,'%s','%s')", ar->FileIndex, ar->JobId, ar->PathId, ar->FilenameId, - ar->attr, sig); + ar->attr, digest); if (!INSERT_DB(jcr, mdb, mdb->cmd)) { Mmsg2(&mdb->errmsg, _("Create db File record %s failed. ERR=%s"), diff --git a/bacula/src/cats/sql_get.c b/bacula/src/cats/sql_get.c index 701332e2d5..c461cba6b5 100644 --- a/bacula/src/cats/sql_get.c +++ b/bacula/src/cats/sql_get.c @@ -136,7 +136,7 @@ int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr) } else { fdbr->FileId = (FileId_t)str_to_int64(row[0]); bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat)); - bstrncpy(fdbr->SIG, row[2], sizeof(fdbr->SIG)); + bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest)); stat = 1; } } else { diff --git a/bacula/src/cats/sql_update.c b/bacula/src/cats/sql_update.c index 3e27669f77..9294d3c68c 100644 --- a/bacula/src/cats/sql_update.c +++ b/bacula/src/cats/sql_update.c @@ -53,16 +53,16 @@ extern int UpdateDB(const char *file, int line, JCR *jcr, B_DB *db, char *update * * ----------------------------------------------------------------------- */ -/* Update the attributes record by adding the MD5 signature */ +/* Update the attributes record by adding the file digest */ int -db_add_SIG_to_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, char *SIG, +db_add_digest_to_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, char *digest, int type) { int stat; - char ed1[50]; + char ed1[CRYPTO_DIGEST_MAX_SIZE]; db_lock(mdb); - Mmsg(mdb->cmd, "UPDATE File SET MD5='%s' WHERE FileId=%s", SIG, + Mmsg(mdb->cmd, "UPDATE File SET MD5='%s' WHERE FileId=%s", digest, edit_int64(FileId, ed1)); stat = UPDATE_DB(jcr, mdb, mdb->cmd); db_unlock(mdb); diff --git a/bacula/src/console/console.c b/bacula/src/console/console.c index 9bea604bd5..7611e9008e 100644 --- a/bacula/src/console/console.c +++ b/bacula/src/console/console.c @@ -396,8 +396,8 @@ int main(int argc, char *argv[]) parse_config(configfile); - if (init_tls() != 0) { - Emsg0(M_ERROR_TERM, 0, _("TLS library initialization failed.\n")); + if (init_crypto() != 0) { + Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n")); } if (!check_resources()) { @@ -549,7 +549,7 @@ static void terminate_console(int sig) exit(1); } already_here = true; - cleanup_tls(); + cleanup_crypto(); free_pool_memory(args); con_term(); (void)WSACleanup(); /* Cleanup Windows sockets */ diff --git a/bacula/src/dird/catreq.c b/bacula/src/dird/catreq.c index 41cebc6d29..5ac184d4f5 100644 --- a/bacula/src/dird/catreq.c +++ b/bacula/src/dird/catreq.c @@ -29,6 +29,7 @@ #include "bacula.h" #include "dird.h" +#include "findlib/find.h" /* * Handle catalog request @@ -396,8 +397,8 @@ void catalog_update(JCR *jcr, BSOCK *bs) ar->Stream = Stream; ar->link = NULL; ar->JobId = jcr->JobId; - ar->Sig = NULL; - ar->SigType = 0; + ar->Digest = NULL; + ar->DigestType = CRYPTO_DIGEST_NONE; jcr->cached_attribute = true; Dmsg2(400, "dirddb)); } #endif - } else if (Stream == STREAM_MD5_SIGNATURE || Stream == STREAM_SHA1_SIGNATURE) { + } else if (crypto_digest_stream_type(Stream) != CRYPTO_DIGEST_NONE) { fname = p; if (ar->FileIndex != FileIndex) { - Jmsg(jcr, M_WARNING, 0, _("Got MD5/SHA1 but not same File as attributes\n")); + Jmsg(jcr, M_WARNING, 0, _("Got %s but not same File as attributes\n"), stream_to_ascii(Stream)); } else { - /* Update signature in catalog */ - char SIGbuf[50]; /* 24 bytes should be enough */ - int len, type; - if (Stream == STREAM_MD5_SIGNATURE) { - len = 16; - type = MD5_SIG; - } else { - len = 20; - type = SHA1_SIG; + /* Update digest in catalog */ + char digestbuf[CRYPTO_DIGEST_MAX_SIZE]; + int len = 0; + int type = CRYPTO_DIGEST_NONE; + + switch(Stream) { + case STREAM_MD5_DIGEST: + len = CRYPTO_DIGEST_MD5_SIZE; + type = CRYPTO_DIGEST_MD5; + break; + case STREAM_SHA1_DIGEST: + len = CRYPTO_DIGEST_SHA1_SIZE; + type = CRYPTO_DIGEST_SHA1; + break; + case STREAM_SHA256_DIGEST: + len = CRYPTO_DIGEST_SHA256_SIZE; + type = CRYPTO_DIGEST_SHA256; + break; + case STREAM_SHA512_DIGEST: + len = CRYPTO_DIGEST_SHA512_SIZE; + type = CRYPTO_DIGEST_SHA512; + break; + default: + /* Never reached ... */ + Jmsg(jcr, M_ERROR, 0, _("Catalog error updating file digest. Unsupported digest stream type: %d"), + Stream); } - bin_to_base64(SIGbuf, fname, len); - Dmsg3(400, "SIGlen=%d SIG=%s type=%d\n", strlen(SIGbuf), SIGbuf, Stream); + + bin_to_base64(digestbuf, fname, len); + Dmsg3(400, "DigestLen=%d Digest=%s type=%d\n", strlen(digestbuf), digestbuf, Stream); if (jcr->cached_attribute) { - ar->Sig = SIGbuf; - ar->SigType = type; - Dmsg2(400, "Cached attr with SIG. Stream=%d fname=%s\n", ar->Stream, ar->fname); + ar->Digest = digestbuf; + ar->DigestType = type; + Dmsg2(400, "Cached attr with digest. Stream=%d fname=%s\n", ar->Stream, ar->fname); if (!db_create_file_attributes_record(jcr, jcr->db, ar)) { Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db)); } jcr->cached_attribute = false; } else { - if (!db_add_SIG_to_file_record(jcr, jcr->db, ar->FileId, SIGbuf, type)) { - Jmsg(jcr, M_ERROR, 0, _("Catalog error updating MD5/SHA1. %s"), + if (!db_add_digest_to_file_record(jcr, jcr->db, ar->FileId, digestbuf, type)) { + Jmsg(jcr, M_ERROR, 0, _("Catalog error updating file digest. %s"), db_strerror(jcr->db)); } } diff --git a/bacula/src/dird/dird.c b/bacula/src/dird/dird.c index eec2b9f2ca..20b32fcdd7 100644 --- a/bacula/src/dird/dird.c +++ b/bacula/src/dird/dird.c @@ -192,8 +192,8 @@ int main (int argc, char *argv[]) parse_config(configfile); - if (init_tls() != 0) { - Jmsg((JCR *)NULL, M_ERROR_TERM, 0, _("TLS library initialization failed.\n")); + if (init_crypto() != 0) { + Jmsg((JCR *)NULL, M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n")); } if (!check_resources()) { @@ -280,7 +280,7 @@ static void terminate_dird(int sig) term_ua_server(); term_msg(); /* terminate message handler */ stop_watchdog(); - cleanup_tls(); + cleanup_crypto(); close_memory_pool(); /* release free memory in pool */ sm_dump(false); exit(sig); diff --git a/bacula/src/dird/fd_cmds.c b/bacula/src/dird/fd_cmds.c index d3c02e1c32..5855adf4e3 100644 --- a/bacula/src/dird/fd_cmds.c +++ b/bacula/src/dird/fd_cmds.c @@ -29,6 +29,7 @@ #include "bacula.h" #include "dird.h" +#include "findlib/find.h" /* Commands sent to File daemon */ static char filesetcmd[] = "fileset%s\n"; /* set full fileset */ @@ -508,7 +509,7 @@ int get_attributes_and_put_in_catalog(JCR *jcr) jcr->FileIndex = 0; Dmsg0(120, "bdird: waiting to receive file attributes\n"); - /* Pickup file attributes and signature */ + /* Pickup file attributes and digest */ while (!fd->errors && (n = bget_dirmsg(fd)) > 0) { /*****FIXME****** improve error handling to stop only on @@ -518,11 +519,11 @@ int get_attributes_and_put_in_catalog(JCR *jcr) long file_index; int stream, len; char *attr, *p, *fn; - char Opts_SIG[MAXSTRING]; /* either Verify opts or MD5/SHA1 signature */ - char SIG[MAXSTRING]; + char Opts_Digest[MAXSTRING]; /* either Verify opts or MD5/SHA1 digest */ + char digest[CRYPTO_DIGEST_MAX_SIZE]; jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen); - if ((len = sscanf(fd->msg, "%ld %d %s", &file_index, &stream, Opts_SIG)) != 3) { + if ((len = sscanf(fd->msg, "%ld %d %s", &file_index, &stream, Opts_Digest)) != 3) { Jmsg(jcr, M_FATAL, 0, _("msglen, fd->msg); set_jcr_job_status(jcr, JS_ErrorTerminated); @@ -554,8 +555,8 @@ int get_attributes_and_put_in_catalog(JCR *jcr) ar.ClientId = jcr->ClientId; ar.PathId = 0; ar.FilenameId = 0; - ar.Sig = NULL; - ar.SigType = 0; + ar.Digest = NULL; + ar.DigestType = CRYPTO_DIGEST_NONE; Dmsg2(111, "dirdfname); Dmsg1(120, "dirdFileId = ar.FileId; - } else if (stream == STREAM_MD5_SIGNATURE || stream == STREAM_SHA1_SIGNATURE) { + } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) { if (jcr->FileIndex != (uint32_t)file_index) { - Jmsg2(jcr, M_ERROR, 0, _("MD5/SHA1 index %d not same as attributes %d\n"), - file_index, jcr->FileIndex); + Jmsg3(jcr, M_ERROR, 0, _("%s index %d not same as attributes %d\n"), + stream_to_ascii(stream), file_index, jcr->FileIndex); set_jcr_job_status(jcr, JS_Error); continue; } - db_escape_string(SIG, Opts_SIG, strlen(Opts_SIG)); - Dmsg2(120, "SIGlen=%d SIG=%s\n", strlen(SIG), SIG); - if (!db_add_SIG_to_file_record(jcr, jcr->db, jcr->FileId, SIG, - stream==STREAM_MD5_SIGNATURE?MD5_SIG:SHA1_SIG)) { + db_escape_string(digest, Opts_Digest, strlen(Opts_Digest)); + Dmsg2(120, "DigestLen=%d Digest=%s\n", strlen(digest), digest); + if (!db_add_digest_to_file_record(jcr, jcr->db, jcr->FileId, digest, + crypto_digest_stream_type(stream))) { Jmsg1(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db)); set_jcr_job_status(jcr, JS_Error); } diff --git a/bacula/src/dird/inc_conf.c b/bacula/src/dird/inc_conf.c index 0ca26731ab..67ebde2ac8 100644 --- a/bacula/src/dird/inc_conf.c +++ b/bacula/src/dird/inc_conf.c @@ -103,7 +103,7 @@ static RES_ITEM options_items[] = { enum { INC_KW_NONE, INC_KW_COMPRESSION, - INC_KW_SIGNATURE, + INC_KW_DIGEST, INC_KW_ENCRYPTION, INC_KW_VERIFY, INC_KW_ONEFS, @@ -129,7 +129,7 @@ enum { */ static struct s_kw FS_option_kw[] = { {"compression", INC_KW_COMPRESSION}, - {"signature", INC_KW_SIGNATURE}, + {"signature", INC_KW_DIGEST}, {"encryption", INC_KW_ENCRYPTION}, {"verify", INC_KW_VERIFY}, {"onefs", INC_KW_ONEFS}, @@ -163,8 +163,10 @@ struct s_fs_opt { * included files. */ static struct s_fs_opt FS_options[] = { - {"md5", INC_KW_SIGNATURE, "M"}, - {"sha1", INC_KW_SIGNATURE, "S"}, + {"md5", INC_KW_DIGEST, "M"}, + {"sha1", INC_KW_DIGEST, "S"}, + {"sha256", INC_KW_DIGEST, "S2"}, + {"sha512", INC_KW_DIGEST, "S3"}, {"gzip", INC_KW_COMPRESSION, "Z6"}, {"gzip1", INC_KW_COMPRESSION, "Z1"}, {"gzip2", INC_KW_COMPRESSION, "Z2"}, diff --git a/bacula/src/dird/job.c b/bacula/src/dird/job.c index 38bc8f9080..13a52f09ac 100644 --- a/bacula/src/dird/job.c +++ b/bacula/src/dird/job.c @@ -644,13 +644,13 @@ bool get_or_create_fileset_record(JCR *jcr) bstrncpy(fsr.FileSet, jcr->fileset->hdr.name, sizeof(fsr.FileSet)); if (jcr->fileset->have_MD5) { struct MD5Context md5c; - unsigned char signature[16]; + unsigned char digest[MD5HashSize]; memcpy(&md5c, &jcr->fileset->md5c, sizeof(md5c)); - MD5Final(signature, &md5c); - bin_to_base64(fsr.MD5, (char *)signature, 16); /* encode 16 bytes */ + MD5Final(digest, &md5c); + bin_to_base64(fsr.MD5, (char *)digest, MD5HashSize); bstrncpy(jcr->fileset->MD5, fsr.MD5, sizeof(jcr->fileset->MD5)); } else { - Jmsg(jcr, M_WARNING, 0, _("FileSet MD5 signature not found.\n")); + Jmsg(jcr, M_WARNING, 0, _("FileSet MD5 digest not found.\n")); } if (!jcr->fileset->ignore_fs_changes || !db_get_fileset_record(jcr, jcr->db, &fsr)) { diff --git a/bacula/src/dird/verify.c b/bacula/src/dird/verify.c index f15233465f..8081532727 100644 --- a/bacula/src/dird/verify.c +++ b/bacula/src/dird/verify.c @@ -498,7 +498,7 @@ int get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId) int stat = JS_Terminated; char buf[MAXSTRING]; POOLMEM *fname = get_pool_memory(PM_MESSAGE); - int do_SIG = NO_SIG; + int do_Digest = CRYPTO_DIGEST_NONE; int32_t file_index = 0; memset(&fdbr, 0, sizeof(FILE_DBR)); @@ -512,7 +512,7 @@ int get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId) * We expect: * FileIndex * Stream - * Options or SIG (MD5/SHA1) + * Options or Digest (MD5/SHA1) * Filename * Attributes * Link name ??? @@ -520,11 +520,11 @@ int get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId) while ((n=bget_dirmsg(fd)) >= 0 && !job_canceled(jcr)) { int stream; char *attr, *p, *fn; - char Opts_SIG[MAXSTRING]; /* Verify Opts or MD5/SHA1 signature */ + char Opts_Digest[MAXSTRING]; /* Verify Opts or MD5/SHA1 digest */ fname = check_pool_memory_size(fname, fd->msglen); jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen); - Dmsg1(200, "Atts+SIG=%s\n", fd->msg); + Dmsg1(200, "Atts+Digest=%s\n", fd->msg); if ((len = sscanf(fd->msg, "%ld %d %100s", &file_index, &stream, fname)) != 3) { Jmsg3(jcr, M_FATAL, 0, _("birdmsg; skip_nonspaces(&p); /* skip FileIndex */ skip_spaces(&p); skip_nonspaces(&p); /* skip Stream */ skip_spaces(&p); - skip_nonspaces(&p); /* skip Opts_SIG */ + skip_nonspaces(&p); /* skip Opts_Digest */ p++; /* skip space */ fn = fname; while (*p != 0) { @@ -558,7 +558,7 @@ int get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId) jcr->JobFiles++; jcr->FileIndex = file_index; /* remember attribute file_index */ decode_stat(attr, &statf, &LinkFIf); /* decode file stat packet */ - do_SIG = NO_SIG; + do_Digest = CRYPTO_DIGEST_NONE; jcr->fn_printed = false; pm_strcpy(jcr->fname, fname); /* move filename into JCR */ @@ -584,13 +584,13 @@ int get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId) } Dmsg3(400, "Found %s in catalog. inx=%d Opts=%s\n", jcr->fname, - file_index, Opts_SIG); + file_index, Opts_Digest); decode_stat(fdbr.LStat, &statc, &LinkFIc); /* decode catalog stat */ /* * Loop over options supplied by user and verify the * fields he requests. */ - for (p=Opts_SIG; *p; p++) { + for (p=Opts_Digest; *p; p++) { char ed1[30], ed2[30]; switch (*p) { case 'i': /* compare INODEs */ @@ -675,10 +675,10 @@ int get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId) break; case '5': /* compare MD5 */ Dmsg1(500, "set Do_MD5 for %s\n", jcr->fname); - do_SIG = MD5_SIG; + do_Digest = CRYPTO_DIGEST_MD5; break; case '1': /* compare SHA1 */ - do_SIG = SHA1_SIG; + do_Digest = CRYPTO_DIGEST_SHA1; break; case ':': case 'V': @@ -687,13 +687,13 @@ int get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId) } } /* - * Got SIG Signature from Storage daemon - * It came across in the Opts_SIG field. + * Got Digest Signature from Storage daemon + * It came across in the Opts_Digest field. */ - } else if (stream == STREAM_MD5_SIGNATURE || stream == STREAM_SHA1_SIGNATURE) { - Dmsg2(400, "stream=SIG inx=%d SIG=%s\n", file_index, Opts_SIG); + } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) { + Dmsg2(400, "stream=Digest inx=%d Digest=%s\n", file_index, Opts_Digest); /* - * When ever we get a signature is MUST have been + * When ever we get a digest is MUST have been * preceded by an attributes record, which sets attr_file_index */ if (jcr->FileIndex != (uint32_t)file_index) { @@ -701,20 +701,20 @@ int get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId) file_index, jcr->FileIndex); return false; } - if (do_SIG) { - db_escape_string(buf, Opts_SIG, strlen(Opts_SIG)); - if (strcmp(buf, fdbr.SIG) != 0) { + if (do_Digest != CRYPTO_DIGEST_NONE) { + db_escape_string(buf, Opts_Digest, strlen(Opts_Digest)); + if (strcmp(buf, fdbr.Digest) != 0) { prt_fname(jcr); if (debug_level >= 10) { Jmsg(jcr, M_INFO, 0, _(" %s not same. File=%s Cat=%s\n"), - stream==STREAM_MD5_SIGNATURE?"MD5":"SHA1", buf, fdbr.SIG); + stream_to_ascii(stream), buf, fdbr.Digest); } else { Jmsg(jcr, M_INFO, 0, _(" %s differs.\n"), - stream==STREAM_MD5_SIGNATURE?"MD5":"SHA1"); + stream_to_ascii(stream)); } stat = JS_Differences; } - do_SIG = FALSE; + do_Digest = CRYPTO_DIGEST_NONE; } } jcr->JobFiles = file_index; diff --git a/bacula/src/filed/Makefile.in b/bacula/src/filed/Makefile.in index ff1dde0cd8..c0e86adb7f 100755 --- a/bacula/src/filed/Makefile.in +++ b/bacula/src/filed/Makefile.in @@ -31,10 +31,10 @@ first_rule: all dummy: # -SVRSRCS = filed.c authenticate.c acl.c backup.c chksum.c estimate.c \ +SVRSRCS = filed.c authenticate.c acl.c backup.c estimate.c \ filed_conf.c heartbeat.c job.c pythonfd.c \ restore.c status.c verify.c verify_vol.c -SVROBJS = filed.o authenticate.o acl.o backup.o chksum.o estimate.o \ +SVROBJS = filed.o authenticate.o acl.o backup.o estimate.o \ filed_conf.o heartbeat.o job.o pythonfd.o \ restore.o status.o verify.o verify_vol.o diff --git a/bacula/src/filed/backup.c b/bacula/src/filed/backup.c index cb489d6114..e25d7d0f86 100644 --- a/bacula/src/filed/backup.c +++ b/bacula/src/filed/backup.c @@ -27,7 +27,7 @@ /* Forward referenced functions */ static int save_file(FF_PKT *ff_pkt, void *pkt, bool top_level); -static int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, struct CHKSUM *chksum); +static int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest, DIGEST *signature_digest); static bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream); static bool read_and_send_acl(JCR *jcr, int acltype, int stream); @@ -125,7 +125,15 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr) static int save_file(FF_PKT *ff_pkt, void *vjcr, bool top_level) { int stat, data_stream; - struct CHKSUM chksum; + DIGEST *digest = NULL; + DIGEST *signing_digest = NULL; + int digest_stream = STREAM_NONE; + // TODO landonf: Allow the user to specify the digest algorithm +#ifdef HAVE_SHA2 + crypto_digest_t signing_algorithm = CRYPTO_DIGEST_SHA256; +#else + crypto_digest_t signing_algorithm = CRYPTO_DIGEST_SHA1; +#endif BSOCK *sd; JCR *jcr = (JCR *)vjcr; @@ -224,13 +232,55 @@ static int save_file(FF_PKT *ff_pkt, void *vjcr, bool top_level) Dmsg1(130, "bfiled: sending %s to stored\n", ff_pkt->fname); + if (!encode_and_send_attributes(jcr, ff_pkt, data_stream)) { + return 0; + } + + /* + * Setup for digest handling. If this fails, the digest will be set to NULL + * and not used. + */ + if (ff_pkt->flags & FO_MD5) { + digest = crypto_digest_new(CRYPTO_DIGEST_MD5); + digest_stream = STREAM_MD5_DIGEST; + + } else if (ff_pkt->flags & FO_SHA1) { + digest = crypto_digest_new(CRYPTO_DIGEST_SHA1); + digest_stream = STREAM_SHA1_DIGEST; + + } else if (ff_pkt->flags & FO_SHA256) { + digest = crypto_digest_new(CRYPTO_DIGEST_SHA256); + digest_stream = STREAM_SHA256_DIGEST; + + } else if (ff_pkt->flags & FO_SHA512) { + digest = crypto_digest_new(CRYPTO_DIGEST_SHA512); + digest_stream = STREAM_SHA512_DIGEST; + } + + /* Did digest initialization fail? */ + if (digest_stream != STREAM_NONE && digest == NULL) { + Jmsg(jcr, M_WARNING, 0, _("%s digest initialization failed\n"), + stream_to_ascii(digest_stream)); + } /* - * Setup for signature handling. - * Then initialise the file descriptor we use for data and other streams. + * Set up signature digest handling. If this fails, the signature digest will be set to + * NULL and not used. */ - chksum_init(&chksum, ff_pkt->flags); + // TODO landonf: We should really only calculate the digest once, for both verification and signing. + if (jcr->pki_sign) { + signing_digest = crypto_digest_new(signing_algorithm); + } + /* Full-stop if a failure occured initializing the signature digest */ + if (jcr->pki_sign && signing_digest == NULL) { + Jmsg(jcr, M_NOTSAVED, 0, _("%s signature digest initialization failed\n"), + stream_to_ascii(signing_algorithm)); + jcr->Errors++; + return 1; + } + + /* Initialise the file descriptor we use for data and other streams. */ binit(&ff_pkt->bfd); if (ff_pkt->flags & FO_PORTABLE) { set_portable_backup(&ff_pkt->bfd); /* disable Win32 BackupRead() */ @@ -243,10 +293,6 @@ static int save_file(FF_PKT *ff_pkt, void *vjcr, bool top_level) } } - if (!encode_and_send_attributes(jcr, ff_pkt, data_stream)) { - return 0; - } - /* * Open any file with data that we intend to save, then save it. * @@ -279,7 +325,7 @@ static int save_file(FF_PKT *ff_pkt, void *vjcr, bool top_level) stop_thread_timer(tid); tid = NULL; } - stat = send_data(jcr, data_stream, ff_pkt, &chksum); + stat = send_data(jcr, data_stream, ff_pkt, digest, signing_digest); bclose(&ff_pkt->bfd); if (!stat) { return 0; @@ -305,7 +351,7 @@ static int save_file(FF_PKT *ff_pkt, void *vjcr, bool top_level) } flags = ff_pkt->flags; ff_pkt->flags &= ~(FO_GZIP|FO_SPARSE); - stat = send_data(jcr, STREAM_MACOS_FORK_DATA, ff_pkt, &chksum); + stat = send_data(jcr, STREAM_MACOS_FORK_DATA, ff_pkt, digest); ff_pkt->flags = flags; bclose(&ff_pkt->bfd); if (!stat) { @@ -318,7 +364,12 @@ static int save_file(FF_PKT *ff_pkt, void *vjcr, bool top_level) Dmsg1(300, "bfiled>stored:header %s\n", sd->msg); memcpy(sd->msg, ff_pkt->hfsinfo.fndrinfo, 32); sd->msglen = 32; - chksum_update(&chksum, (unsigned char *)sd->msg, sd->msglen); + if (digest) { + crypto_digest_update(digest, sd->msg, sd->msglen); + } + if (signature_digest) { + crypto_digest_update(signature_digest, sd->msg, sd->msglen); + } bnet_send(sd); bnet_sig(sd, BNET_EOD); } @@ -337,25 +388,78 @@ static int save_file(FF_PKT *ff_pkt, void *vjcr, bool top_level) } } - /* Terminate any signature and send it to Storage daemon and the Director */ - if (chksum.updated) { - int stream = 0; - chksum_final(&chksum); - if (chksum.type == CHKSUM_MD5) { - stream = STREAM_MD5_SIGNATURE; - } else if (chksum.type == CHKSUM_SHA1) { - stream = STREAM_SHA1_SIGNATURE; - } else { - Jmsg1(jcr, M_WARNING, 0, _("Unknown signature type %i.\n"), chksum.type); + /* Terminate the signing digest and send it to the Storage daemon */ + if (signing_digest) { + SIGNATURE *sig; + size_t size = 0; + void *buf; + + if ((sig = crypto_sign_new()) == NULL) { + Jmsg(jcr, M_FATAL, 0, _("Failed to allocate memory for stream signature.\n")); + return 0; } - if (stream != 0) { - bnet_fsend(sd, "%ld %d 0", jcr->JobFiles, stream); + + if (crypto_sign_add_signer(sig, signing_digest, jcr->pki_keypair) == false) { + Jmsg(jcr, M_FATAL, 0, _("An error occured while signing the stream.\n")); + return 0; + } + + /* Get signature size */ + if (crypto_sign_encode(sig, NULL, &size) == false) { + Jmsg(jcr, M_FATAL, 0, _("An error occured while signing the stream.\n")); + return 0; + } + + /* Allocate signature data buffer */ + buf = malloc(size); + if (!buf) { + free(buf); + return 0; + } + + /* Encode signature data */ + if (crypto_sign_encode(sig, buf, &size) == false) { + Jmsg(jcr, M_FATAL, 0, _("An error occured while signing the stream.\n")); + return 0; + } + + /* Send our header */ + bnet_fsend(sd, "%ld %d 0", jcr->JobFiles, STREAM_SIGNED_DIGEST); + Dmsg1(300, "bfiled>stored:header %s\n", sd->msg); + + /* Grow the bsock buffer to fit our message if necessary */ + if ((size_t) sizeof_pool_memory(sd->msg) < size) { + sd->msg = realloc_pool_memory(sd->msg, size); + } + + /* Copy our message over and send it */ + memcpy(sd->msg, buf, size); + sd->msglen = size; + bnet_send(sd); + bnet_sig(sd, BNET_EOD); /* end of checksum */ + + crypto_digest_free(signing_digest); + crypto_sign_free(sig); + free(buf); + } + + /* Terminate any digest and send it to Storage daemon and the Director */ + if (digest) { + char md[CRYPTO_DIGEST_MAX_SIZE]; + size_t size; + + size = sizeof(md); + + if (crypto_digest_finalize(digest, &md, &size) == true) { + bnet_fsend(sd, "%ld %d 0", jcr->JobFiles, digest_stream); Dmsg1(300, "bfiled>stored:header %s\n", sd->msg); - memcpy(sd->msg, chksum.signature, chksum.length); - sd->msglen = chksum.length; + memcpy(sd->msg, md, size); + sd->msglen = size; bnet_send(sd); bnet_sig(sd, BNET_EOD); /* end of checksum */ } + + crypto_digest_free(digest); } return 1; @@ -371,7 +475,7 @@ static int save_file(FF_PKT *ff_pkt, void *vjcr, bool top_level) * Currently this is not a problem as the only other stream, resource forks, * are not handled as sparse files. */ -static int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, struct CHKSUM *chksum) +int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest, DIGEST *signing_digest) { BSOCK *sd = jcr->store_bsock; uint64_t fileAddr = 0; /* file address */ @@ -461,7 +565,14 @@ static int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, struct CHKSUM *chksum fileAddr += sd->msglen; /* Update checksum if requested */ - chksum_update(chksum, (unsigned char *)rbuf, sd->msglen); + if (digest) { + crypto_digest_update(digest, rbuf, sd->msglen); + } + + /* Update signing digest if requested */ + if (signing_digest) { + crypto_digest_update(signing_digest, rbuf, sd->msglen); + } #ifdef HAVE_LIBZ /* Do compression if turned on */ diff --git a/bacula/src/filed/chksum.c b/bacula/src/filed/chksum.c deleted file mode 100644 index 721d7609da..0000000000 --- a/bacula/src/filed/chksum.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * General routines for handling the various checksum supported. - * - * Written by Preben 'Peppe' Guldberg, December MMIV - */ -/* - Copyright (C) 2004-2005 Kern Sibbald - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - version 2 as amended with additional clauses defined in the - file LICENSE in the main source directory. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - the file LICENSE for additional details. - - */ - -#include "bacula.h" -#include "filed.h" - -/* return 0 on success, otherwise some handler specific error code. */ -int chksum_init(CHKSUM *chksum, int flags) -{ - int status = 0; - - chksum->type = CHKSUM_NONE; - bstrncpy(chksum->name, "NONE", sizeof(chksum->name)); - chksum->updated = false; - if (flags & CHKSUM_MD5) { - chksum->length = 16; - MD5Init(&chksum->context.md5); - chksum->type = CHKSUM_MD5; - bstrncpy(chksum->name, "MD5", sizeof(chksum->name)); - } else if (flags & CHKSUM_SHA1) { - chksum->length = 20; - status = SHA1Init(&chksum->context.sha1); - if (status == 0) { - chksum->type = CHKSUM_SHA1; - bstrncpy(chksum->name, "SHA1", sizeof(chksum->name)); - } - } - return status; -} - -/* return 0 on success, otherwise some handler specific error code. */ -int chksum_update(CHKSUM *chksum, void *buf, unsigned len) -{ - int status; - switch (chksum->type) { - case CHKSUM_NONE: - return 0; - case CHKSUM_MD5: - MD5Update(&chksum->context.md5, (unsigned char *)buf, len); - chksum->updated = true; - return 0; - case CHKSUM_SHA1: - status = SHA1Update(&chksum->context.sha1, (uint8_t *)buf, len); - if (status == 0) { - chksum->updated = true; - } - return status; - default: - return -1; - } -} - -/* return 0 on success, otherwise some handler specific error code. */ -int chksum_final(CHKSUM *chksum) -{ - switch (chksum->type) { - case CHKSUM_NONE: - return 0; - case CHKSUM_MD5: - MD5Final(chksum->signature, &chksum->context.md5); - return 0; - case CHKSUM_SHA1: - return SHA1Final(&chksum->context.sha1, chksum->signature); - default: - return -1; - } -} diff --git a/bacula/src/filed/chksum.h b/bacula/src/filed/chksum.h deleted file mode 100644 index 7be26339d2..0000000000 --- a/bacula/src/filed/chksum.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * General routines for handling the various checksum supported. - */ -/* - Copyright (C) 2000-2005 Kern Sibbald - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - version 2 as amended with additional clauses defined in the - file LICENSE in the main source directory. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - the file LICENSE for additional details. - - */ - -#ifndef _CHKSUM_H_ -#define _CHKSUM_H_ - -#include "bacula.h" - -/* - * Link these to findlib options. Doing so allows for simpler handling of - * signatures in the callers. - * If multiple signatures are specified, the order in chksum_init() matters. - * Still, spell out our own names in case we want to change the approach. - */ -#define CHKSUM_NONE 0 -#define CHKSUM_MD5 FO_MD5 -#define CHKSUM_SHA1 FO_SHA1 - -union chksumContext { - MD5Context md5; - SHA1Context sha1; -}; - -struct CHKSUM { - int type; /* One of CHKSUM_* above */ - char name[5]; /* Big enough for NONE, MD5, SHA1, etc. */ - bool updated; /* True if updated by chksum_update() */ - chksumContext context; /* Context for the algorithm at hand */ - int length; /* Length of signature */ - unsigned char signature[30]; /* Large enough for either signature */ -}; - -int chksum_init(CHKSUM *chksum, int flags); -int chksum_update(CHKSUM *chksum, void *buf, unsigned len); -int chksum_final(CHKSUM *chksum); - -#endif diff --git a/bacula/src/filed/filed.c b/bacula/src/filed/filed.c index fd36fa3df5..fe6b33c37d 100644 --- a/bacula/src/filed/filed.c +++ b/bacula/src/filed/filed.c @@ -179,8 +179,8 @@ int main (int argc, char *argv[]) parse_config(configfile); - if (init_tls() != 0) { - Emsg0(M_ERROR, 0, _("TLS library initialization failed.\n")); + if (init_crypto() != 0) { + Emsg0(M_ERROR, 0, _("Cryptography library initialization failed.\n")); terminate_filed(1); } @@ -260,7 +260,7 @@ void terminate_filed(int sig) free_config_resources(); term_msg(); stop_watchdog(); - cleanup_tls(); + cleanup_crypto(); close_memory_pool(); /* release free memory in pool */ sm_dump(false); /* dump orphaned buffers */ exit(sig); @@ -328,6 +328,75 @@ static int check_resources() OK = false; } } + + if (me->pki_encrypt || me->pki_sign) { +#ifndef HAVE_CRYPTO + Jmsg(NULL, M_FATAL, 0, _("PKI encryption/signing enabled but not compiled into Bacula.\n")); + OK = false; +#endif + } + + /* pki_encrypt implies pki_sign */ + if (me->pki_encrypt) { + me->pki_sign = true; + } + + if ((me->pki_encrypt || me->pki_sign) && !me->pki_keypairfile) { + Emsg2(M_FATAL, 0, _("\"PKI Key Pair\" must be defined for File" + " daemon \"%s\" in %s if either \"PKI Sign\" or" + " \"PKI Encrypt\" are enabled.\n"), me->hdr.name, configfile); + OK = false; + } + + /* If everything is well, attempt to initialize our public/private keys */ + if (OK && (me->pki_encrypt || me->pki_sign)) { + char *filepath; + + /* Load our keypair */ + me->pki_keypair = crypto_keypair_new(); + if (!me->pki_keypair) { + Emsg0(M_FATAL, 0, _("Failed to allocate a new keypair object.\n")); + OK = false; + } else { + if (!crypto_keypair_load_cert(me->pki_keypair, me->pki_keypairfile)) { + Emsg2(M_FATAL, 0, _("Failed to load public certificate for File" + " daemon \"%s\" in %s.\n"), me->hdr.name, configfile); + OK = false; + } + + if (!crypto_keypair_load_key(me->pki_keypair, me->pki_keypairfile, NULL, NULL)) { + Emsg2(M_FATAL, 0, _("Failed to load private key for File" + " daemon \"%s\" in %s.\n"), me->hdr.name, configfile); + OK = false; + } + } + + /* + * Trusted Signers. We're always trusted. me->pki_keypair + * will be deallocated when me->pki_signers is deallocated. + */ + me->pki_signers = New(alist(10, not_owned_by_alist)); + me->pki_signers->append(me->pki_keypair); + + /* If additional trusted keys have been specified, load them up */ + foreach_alist(filepath, me->pki_trustedkeys) { + X509_KEYPAIR *keypair; + + keypair = crypto_keypair_new(); + if (!keypair) { + Emsg0(M_FATAL, 0, _("Failed to allocate a new keypair object.\n")); + OK = false; + } else { + if (crypto_keypair_load_cert(keypair, filepath)) { + me->pki_signers->append(keypair); + } else { + Emsg3(M_FATAL, 0, _("Failed to load trusted signer certificate" + " from file %s for File daemon \"%s\" in %s.\n"), filepath, me->hdr.name, configfile); + OK = false; + } + } + } + } } diff --git a/bacula/src/filed/filed.h b/bacula/src/filed/filed.h index 4137317ee9..43a8e3fa92 100644 --- a/bacula/src/filed/filed.h +++ b/bacula/src/filed/filed.h @@ -27,7 +27,6 @@ #define FILE_DAEMON 1 #include "filed_conf.h" -#include "chksum.h" #include "findlib/find.h" #include "jcr.h" #include "acl.h" diff --git a/bacula/src/filed/filed_conf.c b/bacula/src/filed/filed_conf.c index 1739ce7484..242eab9ab8 100644 --- a/bacula/src/filed/filed_conf.c +++ b/bacula/src/filed/filed_conf.c @@ -89,12 +89,16 @@ static RES_ITEM cli_items[] = { {"heartbeatinterval", store_time, ITEM(res_client.heartbeat_interval), 0, ITEM_DEFAULT, 0}, {"sdconnecttimeout", store_time,ITEM(res_client.SDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30}, {"maximumnetworkbuffersize", store_pint, ITEM(res_client.max_network_buffer_size), 0, 0, 0}, - {"tlsenable", store_yesno, ITEM(res_client.tls_enable), 1, 0, 0}, - {"tlsrequire", store_yesno, ITEM(res_client.tls_require), 1, 0, 0}, - {"tlscacertificatefile", store_dir, ITEM(res_client.tls_ca_certfile), 0, 0, 0}, - {"tlscacertificatedir", store_dir, ITEM(res_client.tls_ca_certdir), 0, 0, 0}, - {"tlscertificate", store_dir, ITEM(res_client.tls_certfile), 0, 0, 0}, - {"tlskey", store_dir, ITEM(res_client.tls_keyfile), 0, 0, 0}, + {"pkisignatures", store_yesno, ITEM(res_client.pki_sign), 1, ITEM_DEFAULT, 0}, + {"pkiencryption", store_yesno, ITEM(res_client.pki_encrypt), 1, ITEM_DEFAULT, 0}, + {"pkikeypair", store_dir, ITEM(res_client.pki_keypairfile), 0, 0, 0}, + {"pkitrustedsigner", store_alist_str, ITEM(res_client.pki_trustedkeys), 0, 0, 0}, + {"tlsenable", store_yesno, ITEM(res_client.tls_enable), 1, 0, 0}, + {"tlsrequire", store_yesno, ITEM(res_client.tls_require), 1, 0, 0}, + {"tlscacertificatefile", store_dir, ITEM(res_client.tls_ca_certfile), 0, 0, 0}, + {"tlscacertificatedir", store_dir, ITEM(res_client.tls_ca_certdir), 0, 0, 0}, + {"tlscertificate", store_dir, ITEM(res_client.tls_certfile), 0, 0, 0}, + {"tlskey", store_dir, ITEM(res_client.tls_keyfile), 0, 0, 0}, {NULL, NULL, NULL, 0, 0, 0} }; @@ -240,6 +244,20 @@ void free_resource(RES *sres, int type) if (res->res_client.FDaddrs) { free_addresses(res->res_client.FDaddrs); } + if (res->res_client.pki_keypairfile) { + free(res->res_client.pki_keypairfile); + } + /* Also frees res_client.pki_keypair */ + if (res->res_client.pki_trustedkeys) { + delete res->res_client.pki_trustedkeys; + } + if (res->res_client.pki_signers) { + X509_KEYPAIR *keypair; + foreach_alist(keypair, res->res_client.pki_signers) { + crypto_keypair_free(keypair); + } + delete res->res_client.pki_signers; + } if (res->res_client.tls_ctx) { free_tls_context(res->res_client.tls_ctx); } @@ -321,6 +339,8 @@ void save_resource(int type, RES_ITEM *items, int pass) if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_dir.hdr.name)) == NULL) { Emsg1(M_ABORT, 0, _("Cannot find Client resource %s\n"), res_all.res_dir.hdr.name); } + res->res_client.pki_trustedkeys = res_all.res_client.pki_trustedkeys; + res->res_client.pki_signers = res_all.res_client.pki_signers; res->res_client.messages = res_all.res_client.messages; break; default: diff --git a/bacula/src/filed/filed_conf.h b/bacula/src/filed/filed_conf.h index 8594228f94..396fae14a7 100644 --- a/bacula/src/filed/filed_conf.h +++ b/bacula/src/filed/filed_conf.h @@ -71,6 +71,10 @@ struct CLIENT { utime_t heartbeat_interval; /* Interval to send heartbeats to Dir */ utime_t SDConnectTimeout; /* timeout in seconds */ uint32_t max_network_buffer_size; /* max network buf size */ + int pki_sign; /* Enable Data Integrity Verification via Digital Signatures */ + int pki_encrypt; /* Enable Data Encryption */ + char *pki_keypairfile; /* PKI Key Pair File */ + alist *pki_trustedkeys; /* PKI Trusted Public Keys */ int tls_enable; /* Enable TLS */ int tls_require; /* Require TLS */ char *tls_ca_certfile; /* TLS CA Certificate File */ @@ -78,6 +82,8 @@ struct CLIENT { char *tls_certfile; /* TLS Client Certificate File */ char *tls_keyfile; /* TLS Client Key File */ + X509_KEYPAIR *pki_keypair; /* Shared PKI Public/Private Keypair */ + alist *pki_signers; /* Shared PKI Trusted Signers */ TLS_CONTEXT *tls_ctx; /* Shared TLS Context */ }; diff --git a/bacula/src/filed/job.c b/bacula/src/filed/job.c index edbac446d9..7af7ce10b0 100644 --- a/bacula/src/filed/job.c +++ b/bacula/src/filed/job.c @@ -173,6 +173,10 @@ void *handle_client_request(void *dirp) jcr->last_fname[0] = 0; jcr->client_name = get_memory(strlen(my_name) + 1); pm_strcpy(jcr->client_name, my_name); + jcr->pki_sign = me->pki_sign; + jcr->pki_encrypt = me->pki_encrypt; + jcr->pki_keypair = me->pki_keypair; + jcr->pki_signers = me->pki_signers; dir->jcr = jcr; enable_backup_privileges(NULL, 1 /* ignore_errors */); @@ -865,7 +869,32 @@ static void set_options(findFOPTS *fo, const char *opts) fo->flags |= FO_READFIFO; break; case 'S': - fo->flags |= FO_SHA1; + switch(*(p + 1)) { + case ' ': + /* Old director did not specify SHA variant */ + fo->flags |= FO_SHA1; + break; + case '1': + fo->flags |= FO_SHA1; + p++; + break; +#ifdef HAVE_SHA2 + case '2': + fo->flags |= FO_SHA256; + p++; + break; + case '3': + fo->flags |= FO_SHA512; + p++; + break; +#endif + default: + /* Automatically downgrade to SHA-1 if an unsupported + * SHA variant is specified */ + fo->flags |= FO_SHA1; + p++; + break; + } break; case 's': fo->flags |= FO_SPARSE; diff --git a/bacula/src/filed/protos.h b/bacula/src/filed/protos.h index 952f56ff11..92d0a3c83c 100644 --- a/bacula/src/filed/protos.h +++ b/bacula/src/filed/protos.h @@ -23,13 +23,16 @@ */ extern bool blast_data_to_storage_daemon(JCR *jcr, char *addr); -extern void do_verify(JCR *jcr); extern void do_verify_volume(JCR *jcr); extern void do_restore(JCR *jcr); extern int authenticate_director(JCR *jcr); extern int authenticate_storagedaemon(JCR *jcr); extern int make_estimate(JCR *jcr); +/* From verify.c */ +int digest_file(JCR *jcr, FF_PKT *ff_pkt, DIGEST *digest); +void do_verify(JCR *jcr); + /* From heartbeat.c */ void start_heartbeat_monitor(JCR *jcr); void stop_heartbeat_monitor(JCR *jcr); diff --git a/bacula/src/filed/restore.c b/bacula/src/filed/restore.c index f218a6a7e1..353233e364 100644 --- a/bacula/src/filed/restore.c +++ b/bacula/src/filed/restore.c @@ -36,6 +36,7 @@ static char rec_header[] = "rechdr %ld %ld %ld %ld %ld"; static const char *zlib_strerror(int stat); #endif +int verify_signature(JCR *jcr, SIGNATURE *sig); int32_t extract_data(JCR *jcr, BFILE *bfd, POOLMEM *buf, int32_t buflen, uint64_t *addr, int flags); @@ -81,6 +82,7 @@ void do_restore(JCR *jcr) BFILE altbfd; /* Alternative data stream */ uint64_t alt_addr = 0; /* Write address for alternative stream */ intmax_t alt_size = 0; /* Size of alternate stream */ + SIGNATURE *sig = NULL; /* Cryptographic signature (if any) for file */ int flags; /* Options for extract_data() */ int stat; ATTR *attr; @@ -137,7 +139,8 @@ void do_restore(JCR *jcr) * or c. Alternate data stream (e.g. Resource Fork) * or d. Finder info * or e. ACLs - * or f. Possibly MD5 or SHA1 record + * or f. Possibly a cryptographic signature + * or g. Possibly MD5 or SHA1 record * 3. Repeat step 1 * * NOTE: We keep track of two bacula file descriptors: @@ -195,7 +198,7 @@ void do_restore(JCR *jcr) Dmsg1(30, "Stream=Unix Attributes. extract=%d\n", extract); /* * If extracting, it was from previous stream, so - * close the output file. + * close the output file and validate the signature. */ if (extract) { if (size > 0 && !is_bopen(&bfd)) { @@ -203,6 +206,23 @@ void do_restore(JCR *jcr) } set_attributes(jcr, attr, &bfd); extract = false; + + /* Verify the cryptographic signature, if any */ + if (jcr->pki_sign) { + if (sig) { + if (!verify_signature(jcr, sig)) { + // TODO landonf: Better signature failure handling. + // The failure is reported to the director in verify_signature() ... + Dmsg1(100, "Bad signature on %s\n", jcr->last_fname); + } else { + Dmsg1(100, "Signature good on %s\n", jcr->last_fname); + } + crypto_sign_free(sig); + sig = NULL; + } else { + Jmsg1(jcr, M_ERROR, 0, _("Missing cryptographic signature for %s\n"), jcr->last_fname); + } + } Dmsg0(30, "Stop extracting.\n"); } else if (is_bopen(&bfd)) { Jmsg0(jcr, M_ERROR, 0, _("Logic error: output file should not be open\n")); @@ -370,8 +390,15 @@ void do_restore(JCR *jcr) #endif break; - case STREAM_MD5_SIGNATURE: - case STREAM_SHA1_SIGNATURE: + case STREAM_SIGNED_DIGEST: + /* Save signature. */ + sig = crypto_sign_decode(sd->msg, (size_t) sd->msglen); + break; + + case STREAM_MD5_DIGEST: + case STREAM_SHA1_DIGEST: + case STREAM_SHA256_DIGEST: + case STREAM_SHA512_DIGEST: break; case STREAM_PROGRAM_NAMES: @@ -475,6 +502,66 @@ static const char *zlib_strerror(int stat) } #endif +static int do_file_digest(FF_PKT *ff_pkt, void *pkt, bool top_level) { + JCR *jcr = (JCR *) pkt; + return (digest_file(jcr, ff_pkt, jcr->digest)); +} + +/* + * Verify the signature for the last restored file + * Return value is either true (signature correct) + * or false (signature could not be verified). + */ +int verify_signature(JCR *jcr, SIGNATURE *sig) +{ + X509_KEYPAIR *keypair; + DIGEST *digest = NULL; + crypto_error_t err; + + + /* Iterate through the trusted signers */ + foreach_alist(keypair, jcr->pki_signers) { + err = crypto_sign_get_digest(sig, jcr->pki_keypair, &digest); + + switch (err) { + case CRYPTO_ERROR_NONE: + /* Signature found, digest allocated */ + jcr->digest = digest; + + /* Checksum the entire file */ + if (find_one_file(jcr, jcr->ff, do_file_digest, jcr, jcr->last_fname, (dev_t)-1, 1) != 0) { + Jmsg(jcr, M_ERROR, 0, _("Signature validation failed for %s: \n"), jcr->last_fname); + return false; + } + + /* Verify the signature */ + if ((err = crypto_sign_verify(sig, keypair, digest)) != CRYPTO_ERROR_NONE) { + Jmsg2(jcr, M_ERROR, 0, _("Signature validation failed for %s: %s\n"), jcr->last_fname, crypto_strerror(err)); + crypto_digest_free(digest); + return false; + } + + /* Valid signature */ + crypto_digest_free(digest); + return true; + + case CRYPTO_ERROR_NOSIGNER: + /* Signature not found, try again */ + continue; + default: + /* Something strange happened (that shouldn't happen!)... */ + Jmsg2(jcr, M_ERROR, 0, _("Signature validation failed for %s: %s\n"), jcr->last_fname, crypto_strerror(err)); + if (digest) { + crypto_digest_free(digest); + } + return false; + } + } + + /* Unreachable */ + return false; +} + /* * In the context of jcr, write data to bfd. * We write buflen bytes in buf at addr. addr is updated in place. diff --git a/bacula/src/filed/verify.c b/bacula/src/filed/verify.c index c25f8294cc..850c959673 100644 --- a/bacula/src/filed/verify.c +++ b/bacula/src/filed/verify.c @@ -30,7 +30,7 @@ #include "filed.h" static int verify_file(FF_PKT *ff_pkt, void *my_pkt, bool); -static int read_chksum(BFILE *bfd, CHKSUM *chksum, JCR *jcr); +static int read_digest(BFILE *bfd, DIGEST *digest, JCR *jcr); /* * Find all the requested files and send attributes @@ -67,9 +67,9 @@ static int verify_file(FF_PKT *ff_pkt, void *pkt, bool top_level) { char attribs[MAXSTRING]; char attribsEx[MAXSTRING]; + int digest_stream = STREAM_NONE; int stat; - BFILE bfd; - struct CHKSUM chksum; + DIGEST *digest = NULL; BSOCK *dir; JCR *jcr = (JCR *)pkt; @@ -201,80 +201,130 @@ static int verify_file(FF_PKT *ff_pkt, void *pkt, bool top_level) * First we initialise, then we read files, other streams and Finder Info. */ if (ff_pkt->type != FT_LNKSAVED && (S_ISREG(ff_pkt->statp.st_mode) && - ff_pkt->flags & (FO_MD5|FO_SHA1))) { - chksum_init(&chksum, ff_pkt->flags); - binit(&bfd); - - if (ff_pkt->statp.st_size > 0 || ff_pkt->type == FT_RAW - || ff_pkt->type == FT_FIFO) { - if ((bopen(&bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0)) < 0) { - ff_pkt->ff_errno = errno; - berrno be; - be.set_errno(bfd.berrno); - Jmsg(jcr, M_NOTSAVED, 1, _(" Cannot open %s: ERR=%s.\n"), - ff_pkt->fname, be.strerror()); - jcr->Errors++; - return 1; - } - read_chksum(&bfd, &chksum, jcr); - bclose(&bfd); + ff_pkt->flags & (FO_MD5|FO_SHA1|FO_SHA256|FO_SHA512))) { + /* + * Create our digest context. If this fails, the digest will be set to NULL + * and not used. + */ + if (ff_pkt->flags & FO_MD5) { + digest = crypto_digest_new(CRYPTO_DIGEST_MD5); + digest_stream = STREAM_MD5_DIGEST; + + } else if (ff_pkt->flags & FO_SHA1) { + digest = crypto_digest_new(CRYPTO_DIGEST_SHA1); + digest_stream = STREAM_SHA1_DIGEST; + + } else if (ff_pkt->flags & FO_SHA256) { + digest = crypto_digest_new(CRYPTO_DIGEST_SHA256); + digest_stream = STREAM_SHA256_DIGEST; + + } else if (ff_pkt->flags & FO_SHA512) { + digest = crypto_digest_new(CRYPTO_DIGEST_SHA512); + digest_stream = STREAM_SHA512_DIGEST; } -#ifdef HAVE_DARWIN_OS - /* Open resource fork if necessary */ - if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->hfsinfo.rsrclength > 0) { - if (bopen_rsrc(&bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) { - ff_pkt->ff_errno = errno; - berrno be; - Jmsg(jcr, M_NOTSAVED, -1, _(" Cannot open resource fork for %s: ERR=%s.\n"), - ff_pkt->fname, be.strerror()); + /* Did digest initialization fail? */ + if (digest_stream != STREAM_NONE && digest == NULL) { + Jmsg(jcr, M_WARNING, 0, _("%s digest initialization failed\n"), + stream_to_ascii(digest_stream)); + } + + /* compute MD5 or SHA1 hash */ + if (digest) { + char md[CRYPTO_DIGEST_MAX_SIZE]; + size_t size; + + size = sizeof(md); + + if (digest_file(jcr, ff_pkt, digest) != 0) { jcr->Errors++; - if (is_bopen(&ff_pkt->bfd)) { - bclose(&ff_pkt->bfd); - } return 1; } - read_chksum(&bfd, &chksum, jcr); - bclose(&bfd); + + if (crypto_digest_finalize(digest, &md, &size) == true) { + char *digest_buf; + const char *digest_name; + + digest_buf = (char *) malloc(BASE64_SIZE(size)); + digest_name = crypto_digest_name(digest); + + bin_to_base64(digest_buf, (char *) md, size); + Dmsg3(400, "send inx=%d %s=%s\n", jcr->JobFiles, digest_name, digest_buf); + bnet_fsend(dir, "%d %d %s *%s-%d*", jcr->JobFiles, digest_stream, digest_buf, + digest_name, jcr->JobFiles); + Dmsg3(20, "bfiled>bdird: %s len=%d: msg=%s\n", digest_name, + dir->msglen, dir->msg); + + free(digest_buf); + } + + crypto_digest_free(digest); } - if (ff_pkt->flags & FO_HFSPLUS) { - chksum_update(&chksum, ((unsigned char *)ff_pkt->hfsinfo.fndrinfo), 32); + } + + return 1; +} + +/* + * Compute message digest for the file specified by ff_pkt. + * In case of errors we need the job control record and file name. + */ +int digest_file(JCR *jcr, FF_PKT *ff_pkt, DIGEST *digest) +{ + BFILE bfd; + + binit(&bfd); + + if (ff_pkt->statp.st_size > 0 || ff_pkt->type == FT_RAW + || ff_pkt->type == FT_FIFO) { + if ((bopen(&bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0)) < 0) { + ff_pkt->ff_errno = errno; + berrno be; + be.set_errno(bfd.berrno); + Jmsg(jcr, M_NOTSAVED, 1, _(" Cannot open %s: ERR=%s.\n"), + ff_pkt->fname, be.strerror()); + return 1; } -#endif + read_digest(&bfd, digest, jcr); + bclose(&bfd); + } - /* compute MD5 or SHA1 hash */ - if (chksum.updated) { - char chksumbuf[40]; /* 24 should do */ - int stream = 0; - - chksum_final(&chksum); - if (chksum.type == CHKSUM_MD5) { - stream = STREAM_MD5_SIGNATURE; - } else if (chksum.type == CHKSUM_SHA1) { - stream = STREAM_SHA1_SIGNATURE; +#ifdef HAVE_DARWIN_OS + /* Open resource fork if necessary */ + if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->hfsinfo.rsrclength > 0) { + if (bopen_rsrc(&bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) { + ff_pkt->ff_errno = errno; + berrno be; + Jmsg(jcr, M_NOTSAVED, -1, _(" Cannot open resource fork for %s: ERR=%s.\n"), + ff_pkt->fname, be.strerror()); + if (is_bopen(&ff_pkt->bfd)) { + bclose(&ff_pkt->bfd); } - bin_to_base64(chksumbuf, (char *)chksum.signature, chksum.length); - Dmsg3(400, "send inx=%d %s=%s\n", jcr->JobFiles, chksum.name, chksumbuf); - bnet_fsend(dir, "%d %d %s *%s-%d*", jcr->JobFiles, stream, chksumbuf, - chksum.name, jcr->JobFiles); - Dmsg3(20, "bfiled>bdird: %s len=%d: msg=%s\n", chksum.name, - dir->msglen, dir->msg); + return 1; } + read_digest(&bfd, digest, jcr); + bclose(&bfd); } - return 1; + if (digest && ff_pkt->flags & FO_HFSPLUS) { + crypto_digest_update(digest, ff_pkt->hfsinfo.fndrinfo, 32); + } +#endif + + return 0; } /* - * Read checksum of bfd, updating chksum + * Read message digest of bfd, updating digest * In case of errors we need the job control record and file name. */ -int read_chksum(BFILE *bfd, CHKSUM *chksum, JCR *jcr) +int read_digest(BFILE *bfd, DIGEST *digest, JCR *jcr) { + char buf[DEFAULT_NETWORK_BUFFER_SIZE]; int64_t n; - while ((n=bread(bfd, jcr->big_buf, jcr->buf_size)) > 0) { - chksum_update(chksum, ((unsigned char *)jcr->big_buf), (int)n); + while ((n=bread(bfd, &buf, sizeof(buf))) > 0) { + crypto_digest_update(digest, &buf, n); jcr->JobBytes += n; jcr->ReadBytes += n; } diff --git a/bacula/src/filed/verify_vol.c b/bacula/src/filed/verify_vol.c index 23940b0920..119edf8d6b 100644 --- a/bacula/src/filed/verify_vol.c +++ b/bacula/src/filed/verify_vol.c @@ -49,6 +49,7 @@ void do_verify_volume(JCR *jcr) uint32_t size; uint32_t VolSessionId, VolSessionTime, file_index; uint32_t record_file_index; + char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)]; int type, stat; sd = jcr->store_bsock; @@ -209,27 +210,42 @@ void do_verify_volume(JCR *jcr) case STREAM_WIN32_GZIP_DATA: case STREAM_GZIP_DATA: case STREAM_SPARSE_GZIP_DATA: + case STREAM_SIGNED_DIGEST: /* Do nothing */ break; - case STREAM_MD5_SIGNATURE: - char MD5buf[30]; - bin_to_base64(MD5buf, (char *)sd->msg, 16); /* encode 16 bytes */ - Dmsg2(400, "send inx=%d MD5=%s\n", jcr->JobFiles, MD5buf); - bnet_fsend(dir, "%d %d %s *MD5-%d*", jcr->JobFiles, STREAM_MD5_SIGNATURE, MD5buf, - jcr->JobFiles); + case STREAM_MD5_DIGEST: + bin_to_base64(digest, (char *)sd->msg, CRYPTO_DIGEST_MD5_SIZE); + Dmsg2(400, "send inx=%d MD5=%s\n", jcr->JobFiles, digest); + bnet_fsend(dir, "%d %d %s *MD5-%d*", jcr->JobFiles, STREAM_MD5_DIGEST, digest, + jcr->JobFiles); Dmsg2(20, "bfiled>bdird: MD5 len=%d: msg=%s\n", dir->msglen, dir->msg); - break; + break; - case STREAM_SHA1_SIGNATURE: - char SHA1buf[30]; - bin_to_base64(SHA1buf, (char *)sd->msg, 20); /* encode 20 bytes */ - Dmsg2(400, "send inx=%d SHA1=%s\n", jcr->JobFiles, SHA1buf); - bnet_fsend(dir, "%d %d %s *SHA1-%d*", jcr->JobFiles, STREAM_SHA1_SIGNATURE, - SHA1buf, jcr->JobFiles); + case STREAM_SHA1_DIGEST: + bin_to_base64(digest, (char *)sd->msg, CRYPTO_DIGEST_SHA1_SIZE); + Dmsg2(400, "send inx=%d SHA1=%s\n", jcr->JobFiles, digest); + bnet_fsend(dir, "%d %d %s *SHA1-%d*", jcr->JobFiles, STREAM_SHA1_DIGEST, + digest, jcr->JobFiles); Dmsg2(20, "bfiled>bdird: SHA1 len=%d: msg=%s\n", dir->msglen, dir->msg); - break; + break; + + case STREAM_SHA256_DIGEST: + bin_to_base64(digest, (char *)sd->msg, CRYPTO_DIGEST_SHA256_SIZE); + Dmsg2(400, "send inx=%d SHA256=%s\n", jcr->JobFiles, digest); + bnet_fsend(dir, "%d %d %s *SHA256-%d*", jcr->JobFiles, STREAM_SHA256_DIGEST, + digest, jcr->JobFiles); + Dmsg2(20, "bfiled>bdird: SHA256 len=%d: msg=%s\n", dir->msglen, dir->msg); + break; + + case STREAM_SHA512_DIGEST: + bin_to_base64(digest, (char *)sd->msg, CRYPTO_DIGEST_SHA512_SIZE); + Dmsg2(400, "send inx=%d SHA512=%s\n", jcr->JobFiles, digest); + bnet_fsend(dir, "%d %d %s *SHA512-%d*", jcr->JobFiles, STREAM_SHA512_DIGEST, + digest, jcr->JobFiles); + Dmsg2(20, "bfiled>bdird: SHA512 len=%d: msg=%s\n", dir->msglen, dir->msg); + break; default: Pmsg2(0, "None of above!!! stream=%d data=%s\n", stream,sd->msg); diff --git a/bacula/src/findlib/bfile.c b/bacula/src/findlib/bfile.c index 61cd8dc797..f9f66652d9 100644 --- a/bacula/src/findlib/bfile.c +++ b/bacula/src/findlib/bfile.c @@ -70,8 +70,8 @@ const char *stream_to_ascii(int stream) return _("File attributes"); case STREAM_FILE_DATA: return _("File data"); - case STREAM_MD5_SIGNATURE: - return _("MD5 signature"); + case STREAM_MD5_DIGEST: + return _("MD5 digest"); case STREAM_UNIX_ATTRIBUTES_EX: return _("Extended attributes"); case STREAM_SPARSE_DATA: @@ -80,12 +80,18 @@ const char *stream_to_ascii(int stream) return _("Program names"); case STREAM_PROGRAM_DATA: return _("Program data"); - case STREAM_SHA1_SIGNATURE: - return _("SHA1 signature"); + case STREAM_SHA1_DIGEST: + return _("SHA1 digest"); case STREAM_MACOS_FORK_DATA: return _("HFS+ resource fork"); case STREAM_HFSPLUS_ATTRIBUTES: return _("HFS+ Finder Info"); + case STREAM_SHA256_DIGEST: + return _("SHA256 digest"); + case STREAM_SHA512_DIGEST: + return _("SHA512 digest"); + case STREAM_SIGNED_DIGEST: + return _("Signed digest"); default: sprintf(buf, "%d", stream); return (const char *)buf; @@ -322,12 +328,19 @@ bool is_restore_stream_supported(int stream) case STREAM_WIN32_DATA: case STREAM_UNIX_ATTRIBUTES: case STREAM_FILE_DATA: - case STREAM_MD5_SIGNATURE: + case STREAM_MD5_DIGEST: case STREAM_UNIX_ATTRIBUTES_EX: case STREAM_SPARSE_DATA: case STREAM_PROGRAM_NAMES: case STREAM_PROGRAM_DATA: - case STREAM_SHA1_SIGNATURE: + case STREAM_SHA1_DIGEST: +#ifdef HAVE_SHA2 + case STREAM_SHA256_DIGEST: + case STREAM_SHA512_DIGEST: +#endif +#ifdef HAVE_CRYPTO + case STREAM_SIGNED_DIGEST: +#endif case 0: /* compatibility with old tapes */ return true; } @@ -691,12 +704,16 @@ bool is_restore_stream_supported(int stream) case STREAM_WIN32_DATA: case STREAM_UNIX_ATTRIBUTES: case STREAM_FILE_DATA: - case STREAM_MD5_SIGNATURE: + case STREAM_MD5_DIGEST: case STREAM_UNIX_ATTRIBUTES_EX: case STREAM_SPARSE_DATA: case STREAM_PROGRAM_NAMES: case STREAM_PROGRAM_DATA: - case STREAM_SHA1_SIGNATURE: + case STREAM_SHA1_DIGEST: +#ifdef HAVE_SHA2 + case STREAM_SHA256_DIGEST: + case STREAM_SHA512_DIGEST: +#endif #ifdef HAVE_DARWIN_OS case STREAM_MACOS_FORK_DATA: case STREAM_HFSPLUS_ATTRIBUTES: diff --git a/bacula/src/findlib/find.h b/bacula/src/findlib/find.h index cb1e44e002..752d009fe0 100755 --- a/bacula/src/findlib/find.h +++ b/bacula/src/findlib/find.h @@ -86,6 +86,8 @@ enum { #define FO_IGNORECASE (1<<16) /* Ignore file name case */ #define FO_HFSPLUS (1<<17) /* Resource forks and Finder Info */ #define FO_WIN32DECOMP (1<<18) /* Use BackupRead decomposition */ +#define FO_SHA256 (1<<19) /* Do SHA256 checksum */ +#define FO_SHA512 (1<<20) /* Do SHA512 checksum */ struct s_included_file { struct s_included_file *next; diff --git a/bacula/src/gnome2-console/console.c b/bacula/src/gnome2-console/console.c index 38fc765ac9..17643ef706 100644 --- a/bacula/src/gnome2-console/console.c +++ b/bacula/src/gnome2-console/console.c @@ -267,8 +267,8 @@ int main(int argc, char *argv[]) parse_config(configfile); - if (init_tls() != 0) { - Emsg0(M_ERROR_TERM, 0, _("TLS library initialization failed.\n")); + if (init_crypto() != 0) { + Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n")); } if (!check_resources()) { @@ -672,7 +672,7 @@ void terminate_console(int sig) if (already_here) /* avoid recursive temination problems */ exit(1); already_here = true; - cleanup_tls(); + cleanup_crypto(); disconnect_from_director((gpointer)NULL); gtk_main_quit(); exit(0); diff --git a/bacula/src/jcr.h b/bacula/src/jcr.h index 4bd623ea72..65d176357c 100644 --- a/bacula/src/jcr.h +++ b/bacula/src/jcr.h @@ -229,6 +229,11 @@ public: volatile BSOCK *hb_bsock; /* duped SD socket */ volatile BSOCK *hb_dir_bsock; /* duped DIR socket */ POOLMEM *RunAfterJob; /* Command to run after job */ + bool pki_sign; /* Enable PKI Signatures? */ + bool pki_encrypt; /* Enable PKI Encryption? */ + DIGEST *digest; /* Last file's digest context */ + X509_KEYPAIR *pki_keypair; /* Encryption key pair */ + alist *pki_signers; /* Trusted Signers */ DIRRES* director; /* Director resource */ #endif /* FILE_DAEMON */ diff --git a/bacula/src/lib/Makefile.in b/bacula/src/lib/Makefile.in index 862ebf8d80..0e2e40e0ff 100644 --- a/bacula/src/lib/Makefile.in +++ b/bacula/src/lib/Makefile.in @@ -25,9 +25,9 @@ dummy: LIBSRCS = alloc.c attr.c base64.c berrno.c bsys.c bget_msg.c \ bnet.c bnet_server.c \ bpipe.c bshm.c bsnprintf.c btime.c \ - cram-md5.c crc32.c daemon.c edit.c fnmatch.c \ + cram-md5.c crc32.c crypto.c daemon.c edit.c fnmatch.c \ hmac.c idcache.c jcr.c lex.c alist.c dlist.c \ - md5.c message.c mem_pool.c parse_conf.c \ + md5.c message.c mem_pool.c openssl.c parse_conf.c \ queue.c regex.c \ res.c rwlock.c scan.c serial.c sha1.c \ semlock.c signal.c smartall.c tls.c tree.c \ @@ -38,9 +38,9 @@ LIBSRCS = alloc.c attr.c base64.c berrno.c bsys.c bget_msg.c \ LIBOBJS = alloc.o attr.o base64.o berrno.o bsys.o bget_msg.o \ bnet.o bnet_server.o \ bpipe.o bshm.o bsnprintf.o btime.o \ - cram-md5.o crc32.o daemon.o edit.o fnmatch.o \ + cram-md5.o crc32.o crypto.o daemon.o edit.o fnmatch.o \ hmac.o idcache.o jcr.o lex.o alist.o dlist.o \ - md5.o message.o mem_pool.o parse_conf.o \ + md5.o message.o mem_pool.o openssl.o parse_conf.o \ queue.o regex.o \ res.o rwlock.o scan.o serial.o sha1.o \ semlock.o signal.o smartall.o tls.o tree.o \ diff --git a/bacula/src/lib/base64.h b/bacula/src/lib/base64.h new file mode 100644 index 0000000000..ae33182437 --- /dev/null +++ b/bacula/src/lib/base64.h @@ -0,0 +1,30 @@ +/* + * Generic base 64 input and output routines + * + * Written by Kern E. Sibbald, March MM. + * + * Version $Id$ + */ + +/* + Copyright (C) 2000-2005 Kern Sibbald and John Walker + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + */ + +/* Maximum size of len bytes after base64 encoding */ +#define BASE64_SIZE(len) (((len + 3 - (len % 3)) / 3) * 4) diff --git a/bacula/src/lib/crypto.c b/bacula/src/lib/crypto.c new file mode 100644 index 0000000000..d2a2d4a4d3 --- /dev/null +++ b/bacula/src/lib/crypto.c @@ -0,0 +1,1078 @@ +/* + * crypto.c Encryption support functions + * + * Author: Landon Fuller + * + * Version $Id$ + * + * Copyright (C) 2005 Kern Sibbald + * + * This file was contributed to the Bacula project by Landon Fuller. + * + * Landon Fuller has been granted a perpetual, worldwide, non-exclusive, + * no-charge, royalty-free, irrevocable copyright license to reproduce, + * prepare derivative works of, publicly display, publicly perform, + * sublicense, and distribute the original work contributed by Landon Fuller + * to the Bacula project in source or object form. + * + * If you wish to license these contributions under an alternate open source + * license please contact Landon Fuller . + */ +/* + Copyright (C) 2005 Kern Sibbald + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as amended with additional clauses defined in the + file LICENSE in the main source directory. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + the file LICENSE for additional details. + + */ + + +#include "bacula.h" +#include + +/* + * Bacula ASN.1 Syntax + * + * OID Allocation: + * Prefix: iso.org.dod.internet.private.enterprise.threerings.external.bacula (1.3.6.1.4.1.22054.500.2) + * Organization: Bacula Project + * Contact Name: Kern Sibbald + * Contact E-mail: kern@sibbald.com + * + * Top Level Allocations - 500.2 + * 1 - Published Allocations + * 1.1 - Bacula Encryption + * + * Bacula Encryption - 500.2.1.1 + * 1 - ASN.1 Modules + * 1.1 - BaculaCrypto + * 2 - ASN.1 Object Identifiers + * 2.1 - SignatureData + * 2.2 - SignerInfo + * 2.3 - CryptoData + * 2.4 - RecipientInfo + * + * BaculaCrypto { iso(1) identified-organization(3) usdod(6) + * internet(1) private(4) enterprises(1) three-rings(22054) + * external(500) bacula(2) published(1) bacula-encryption(1) + * asn1-modules(1) bacula-crypto(1) } + * + * DEFINITIONS AUTOMATIC TAGS ::= + * BEGIN + * + * SignatureData ::= SEQUENCE { + * version Version DEFAULT v0, + * signerInfo SignerInfo } + * + * CryptoData ::= SEQUENCE { + * version Version DEFAULT v0, + * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier + * recipientInfo RecipientInfo + * } + * + * SignerInfo ::= SET OF SignerInfo + * RecipientInfo ::= SET OF RecipientInfo + * + * Version ::= INTEGER { v0(0) } + * + * SignerInfo ::= SEQUENCE { + * version Version, + * subjectKeyIdentifier SubjectKeyIdentifier, + * digestAlgorithm DigestAlgorithmIdentifier, + * signatureAlgorithm SignatureAlgorithmIdentifier, + * signature SignatureValue } + * + * RecipientInfo ::= SEQUENCE { + * version Version + * subjectKeyIdentifier SubjectKeyIdentifier + * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier + * encryptedKey EncryptedKey + * } + * + * SubjectKeyIdentifier ::= OCTET STRING + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * SignatureAlgorithmIdentifier ::= AlgorithmIdentifier + * + * KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + * + * SignatureValue ::= OCTET STRING + * + * EncryptedKey ::= OCTET STRING + * + * AlgorithmIdentifier ::= OBJECT IDENTIFIER + * + * END + */ + +#ifdef HAVE_CRYPTO /* Is encryption enabled? */ +#ifdef HAVE_OPENSSL /* How about OpenSSL? */ + +/* Are we initialized? */ +static int crypto_initialized = false; + +/* ASN.1 Declarations */ +#define BACULA_ASN1_VERSION 0 + +typedef struct { + ASN1_INTEGER *version; + ASN1_OCTET_STRING *subjectKeyIdentifier; + ASN1_OBJECT *digestAlgorithm; + ASN1_OBJECT *signatureAlgorithm; + ASN1_OCTET_STRING *signature; +} SignerInfo; + +typedef struct { + ASN1_INTEGER *version; + ASN1_OCTET_STRING *subjectKeyIdentifier; + ASN1_OBJECT *keyEncryptionAlgorithm; + ASN1_OCTET_STRING *encryptedKey; +} RecipientInfo; + +ASN1_SEQUENCE(SignerInfo) = { + ASN1_SIMPLE(SignerInfo, version, ASN1_INTEGER), + ASN1_SIMPLE(SignerInfo, subjectKeyIdentifier, ASN1_OCTET_STRING), + ASN1_SIMPLE(SignerInfo, digestAlgorithm, ASN1_OBJECT), + ASN1_SIMPLE(SignerInfo, signatureAlgorithm, ASN1_OBJECT), + ASN1_SIMPLE(SignerInfo, signature, ASN1_OCTET_STRING) +} ASN1_SEQUENCE_END(SignerInfo); + +ASN1_SEQUENCE(RecipientInfo) = { + ASN1_SIMPLE(RecipientInfo, version, ASN1_INTEGER), + ASN1_SIMPLE(RecipientInfo, subjectKeyIdentifier, ASN1_OCTET_STRING), + ASN1_SIMPLE(RecipientInfo, keyEncryptionAlgorithm, ASN1_OBJECT), + ASN1_SIMPLE(RecipientInfo, encryptedKey, ASN1_OCTET_STRING), +} ASN1_SEQUENCE_END(RecipientInfo); + +typedef struct { + ASN1_INTEGER *version; + STACK_OF(SignerInfo) *signerInfo; +} SignatureData; + +typedef struct { + ASN1_INTEGER *version; + ASN1_OBJECT *contentEncryptionAlgorithm; + STACK_OF(RecipientInfo) *recipientInfo; +} CryptoData; + +ASN1_SEQUENCE(SignatureData) = { + ASN1_SIMPLE(SignatureData, version, ASN1_INTEGER), + ASN1_SET_OF(SignatureData, signerInfo, SignerInfo), +} ASN1_SEQUENCE_END(SignatureData); + +ASN1_SEQUENCE(CryptoData) = { + ASN1_SIMPLE(CryptoData, version, ASN1_INTEGER), + ASN1_SET_OF(CryptoData, recipientInfo, RecipientInfo) +} ASN1_SEQUENCE_END(CryptoData); + +IMPLEMENT_ASN1_FUNCTIONS(SignerInfo) +IMPLEMENT_ASN1_FUNCTIONS(SignatureData) +IMPLEMENT_ASN1_FUNCTIONS(CryptoData) +IMPLEMENT_STACK_OF(SignerInfo) +IMPLEMENT_STACK_OF(RecipientInfo) + +/* + * SignerInfo and RecipientInfo stack macros, generated by OpenSSL's util/mkstack.pl. + */ +#define sk_SignerInfo_new(st) SKM_sk_new(SignerInfo, (st)) +#define sk_SignerInfo_new_null() SKM_sk_new_null(SignerInfo) +#define sk_SignerInfo_free(st) SKM_sk_free(SignerInfo, (st)) +#define sk_SignerInfo_num(st) SKM_sk_num(SignerInfo, (st)) +#define sk_SignerInfo_value(st, i) SKM_sk_value(SignerInfo, (st), (i)) +#define sk_SignerInfo_set(st, i, val) SKM_sk_set(SignerInfo, (st), (i), (val)) +#define sk_SignerInfo_zero(st) SKM_sk_zero(SignerInfo, (st)) +#define sk_SignerInfo_push(st, val) SKM_sk_push(SignerInfo, (st), (val)) +#define sk_SignerInfo_unshift(st, val) SKM_sk_unshift(SignerInfo, (st), (val)) +#define sk_SignerInfo_find(st, val) SKM_sk_find(SignerInfo, (st), (val)) +#define sk_SignerInfo_delete(st, i) SKM_sk_delete(SignerInfo, (st), (i)) +#define sk_SignerInfo_delete_ptr(st, ptr) SKM_sk_delete_ptr(SignerInfo, (st), (ptr)) +#define sk_SignerInfo_insert(st, val, i) SKM_sk_insert(SignerInfo, (st), (val), (i)) +#define sk_SignerInfo_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(SignerInfo, (st), (cmp)) +#define sk_SignerInfo_dup(st) SKM_sk_dup(SignerInfo, st) +#define sk_SignerInfo_pop_free(st, free_func) SKM_sk_pop_free(SignerInfo, (st), (free_func)) +#define sk_SignerInfo_shift(st) SKM_sk_shift(SignerInfo, (st)) +#define sk_SignerInfo_pop(st) SKM_sk_pop(SignerInfo, (st)) +#define sk_SignerInfo_sort(st) SKM_sk_sort(SignerInfo, (st)) +#define sk_SignerInfo_is_sorted(st) SKM_sk_is_sorted(SignerInfo, (st)) + +#define d2i_ASN1_SET_OF_SignerInfo(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(SignerInfo, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_SignerInfo(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(SignerInfo, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_SignerInfo(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(SignerInfo, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_SignerInfo(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(SignerInfo, (buf), (len), (d2i_func), (free_func)) + +#define sk_RecipientInfo_new(st) SKM_sk_new(RecipientInfo, (st)) +#define sk_RecipientInfo_new_null() SKM_sk_new_null(RecipientInfo) +#define sk_RecipientInfo_free(st) SKM_sk_free(RecipientInfo, (st)) +#define sk_RecipientInfo_num(st) SKM_sk_num(RecipientInfo, (st)) +#define sk_RecipientInfo_value(st, i) SKM_sk_value(RecipientInfo, (st), (i)) +#define sk_RecipientInfo_set(st, i, val) SKM_sk_set(RecipientInfo, (st), (i), (val)) +#define sk_RecipientInfo_zero(st) SKM_sk_zero(RecipientInfo, (st)) +#define sk_RecipientInfo_push(st, val) SKM_sk_push(RecipientInfo, (st), (val)) +#define sk_RecipientInfo_unshift(st, val) SKM_sk_unshift(RecipientInfo, (st), (val)) +#define sk_RecipientInfo_find(st, val) SKM_sk_find(RecipientInfo, (st), (val)) +#define sk_RecipientInfo_delete(st, i) SKM_sk_delete(RecipientInfo, (st), (i)) +#define sk_RecipientInfo_delete_ptr(st, ptr) SKM_sk_delete_ptr(RecipientInfo, (st), (ptr)) +#define sk_RecipientInfo_insert(st, val, i) SKM_sk_insert(RecipientInfo, (st), (val), (i)) +#define sk_RecipientInfo_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(RecipientInfo, (st), (cmp)) +#define sk_RecipientInfo_dup(st) SKM_sk_dup(RecipientInfo, st) +#define sk_RecipientInfo_pop_free(st, free_func) SKM_sk_pop_free(RecipientInfo, (st), (free_func)) +#define sk_RecipientInfo_shift(st) SKM_sk_shift(RecipientInfo, (st)) +#define sk_RecipientInfo_pop(st) SKM_sk_pop(RecipientInfo, (st)) +#define sk_RecipientInfo_sort(st) SKM_sk_sort(RecipientInfo, (st)) +#define sk_RecipientInfo_is_sorted(st) SKM_sk_is_sorted(RecipientInfo, (st)) + +#define d2i_ASN1_SET_OF_RecipientInfo(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(RecipientInfo, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_RecipientInfo(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(RecipientInfo, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_RecipientInfo(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(RecipientInfo, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_RecipientInfo(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(RecipientInfo, (buf), (len), (d2i_func), (free_func)) +/* End of util/mkstack.pl block */ + +/* X509 Public/Private Key Pair Structure */ +struct X509_Keypair { + ASN1_OCTET_STRING *keyid; + EVP_PKEY *pubkey; + EVP_PKEY *privkey; +}; + +/* Message Digest Structure */ +struct Digest { + crypto_digest_t type; + EVP_MD_CTX ctx; +}; + +/* Message Signature Structure */ +struct Signature { + SignatureData *sigData; +}; + +/* PEM Password Dispatch Context */ +typedef struct PEM_CB_Context { + CRYPTO_PEM_PASSWD_CB *pem_callback; + const void *pem_userdata; +} PEM_CB_CONTEXT; + +/* + * Extract subjectKeyIdentifier from x509 certificate. + * Returns: On success, an ASN1_OCTET_STRING that must be freed via M_ASN1_OCTET_STRING_free(). + * NULL on failure. + */ +static ASN1_OCTET_STRING *openssl_cert_keyid(X509 *cert){ + X509_EXTENSION *ext; + X509V3_EXT_METHOD *method; + ASN1_OCTET_STRING *keyid; + int i; +#if (OPENSSL_VERSION_NUMBER >= 0x0090800FL) + const unsigned char *ext_value_data; +#else + unsigned char *ext_value_data; +#endif + + + /* Find the index to the subjectKeyIdentifier extension */ + i = X509_get_ext_by_NID(cert, NID_subject_key_identifier, -1); + if (i < 0) { + /* Not found */ + return NULL; + } + + /* Grab the extension */ + ext = X509_get_ext(cert, i); + + /* Get x509 extension method structure */ + if (!(method = X509V3_EXT_get(ext))) { + return NULL; + } + + ext_value_data = ext->value->data; + +#if (OPENSSL_VERSION_NUMBER > 0x00907000L) + if (method->it) { + /* New style ASN1 */ + + /* Decode ASN1 item in data */ + keyid = (ASN1_OCTET_STRING *) ASN1_item_d2i(NULL, &ext_value_data, ext->value->length, + ASN1_ITEM_ptr(method->it)); + } else { + /* Old style ASN1 */ + + /* Decode ASN1 item in data */ + keyid = (ASN1_OCTET_STRING *) method->d2i(NULL, &ext_value_data, ext->value->length); + } + +#else + keyid = (ASN1_OCTET_STRING *) method->d2i(NULL, &ext_value_data, ext->value->length); +#endif + + return keyid; +} + +/* + * Create a new keypair object. + * Returns: A pointer to a X509 KEYPAIR object on success. + * NULL on failure. + */ +X509_KEYPAIR *crypto_keypair_new (void) { + X509_KEYPAIR *keypair; + + /* Allocate our keypair structure */ + keypair = (X509_KEYPAIR *) malloc(sizeof(X509_KEYPAIR)); + if (!keypair) { + return NULL; + } + + /* Initialize our keypair structure */ + keypair->keyid = NULL; + keypair->pubkey = NULL; + keypair->privkey = NULL; + + return keypair; +} + +/* + * Load a public key from a PEM-encoded x509 certificate. + * Returns: true on success + * false on failure + */ +int crypto_keypair_load_cert (X509_KEYPAIR *keypair, const char *file) +{ + BIO *bio; + X509 *cert; + + /* Open the file */ + if (!(bio = BIO_new_file(file, "r"))) { + openssl_post_errors(M_ERROR, _("Unable to open certificate file")); + return false; + } + + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); + BIO_free(bio); + if (!cert) { + openssl_post_errors(M_ERROR, _("Unable to read certificate from file")); + return false; + } + + /* Extract the public key */ + if (!(keypair->pubkey = X509_get_pubkey(cert))) { + openssl_post_errors(M_ERROR, _("Unable to extract public key from certificate")); + goto err; + } + + /* Extract the subjectKeyIdentifier extension field */ + if ((keypair->keyid = openssl_cert_keyid(cert)) == NULL) { + Emsg0(M_ERROR, 0, _("Provided certificate does not include the required subjectKeyIdentifier extension.")); + goto err; + } + + /* Validate the public key type (only RSA is supported) */ + if (EVP_PKEY_type(keypair->pubkey->type) != EVP_PKEY_RSA) { + Emsg1(M_ERROR, 0, _("Unsupported key type provided: %d\n"), EVP_PKEY_type(keypair->pubkey->type)); + goto err; + } + + return true; + +err: + X509_free(cert); + if (keypair->pubkey) { + EVP_PKEY_free(keypair->pubkey); + } + return false; +} + +/* Dispatch user PEM encryption callbacks */ +static int crypto_pem_callback_dispatch (char *buf, int size, int rwflag, void *userdata) +{ + PEM_CB_CONTEXT *ctx = (PEM_CB_CONTEXT *) userdata; + return (ctx->pem_callback(buf, size, ctx->pem_userdata)); +} + +/* + * Load a PEM-encoded private key. + * Returns: true on success + * false on failure + */ +int crypto_keypair_load_key (X509_KEYPAIR *keypair, const char *file, + CRYPTO_PEM_PASSWD_CB *pem_callback, + const void *pem_userdata) +{ + BIO *bio; + PEM_CB_CONTEXT ctx; + + /* Open the file */ + if (!(bio = BIO_new_file(file, "r"))) { + openssl_post_errors(M_ERROR, _("Unable to open private key file")); + return false; + } + + /* Set up PEM encryption callback */ + if (pem_callback) { + ctx.pem_callback = pem_callback; + ctx.pem_userdata = pem_userdata; + } else { + ctx.pem_callback = crypto_default_pem_callback; + ctx.pem_userdata = NULL; + } + + keypair->privkey = PEM_read_bio_PrivateKey(bio, NULL, crypto_pem_callback_dispatch, &ctx); + BIO_free(bio); + if (!keypair->privkey) { + openssl_post_errors(M_ERROR, _("Unable to read private key from file")); + return false; + } + + return true; +} + +/* + * Free memory associated with a keypair object. + */ +void crypto_keypair_free (X509_KEYPAIR *keypair) +{ + if (keypair->pubkey) { + EVP_PKEY_free(keypair->pubkey); + } + if (keypair->privkey) { + EVP_PKEY_free(keypair->privkey); + } + if (keypair->keyid) { + M_ASN1_OCTET_STRING_free(keypair->keyid); + } + free(keypair); +} + +/* + * Create a new message digest context of the specified type + * Returns: A pointer to a DIGEST object on success. + * NULL on failure. + */ +DIGEST *crypto_digest_new (crypto_digest_t type) +{ + DIGEST *digest; + const EVP_MD *md = NULL; /* Quell invalid uninitialized warnings */ + + digest = (DIGEST *) malloc(sizeof(DIGEST)); + digest->type = type; + + /* Initialize the OpenSSL message digest context */ + EVP_MD_CTX_init(&digest->ctx); + + /* Determine the correct OpenSSL message digest type */ + switch (type) { + case CRYPTO_DIGEST_MD5: + md = EVP_md5(); + break; + case CRYPTO_DIGEST_SHA1: + md = EVP_sha1(); + break; +#ifdef HAVE_SHA2 + case CRYPTO_DIGEST_SHA256: + md = EVP_sha256(); + break; + case CRYPTO_DIGEST_SHA512: + md = EVP_sha512(); + break; +#endif + default: + Emsg1(M_ERROR, 0, _("Unsupported digest type: %d\n"), type); + goto err; + } + + /* Initialize the backing OpenSSL context */ + if (EVP_DigestInit_ex(&digest->ctx, md, NULL) == 0) { + goto err; + } + + return digest; + +err: + /* This should not happen, but never say never ... */ + openssl_post_errors(M_ERROR, _("OpenSSL digest initialization failed")); + crypto_digest_free(digest); + return NULL; +} + +/* + * Hash length bytes of data into the provided digest context. + * Returns: true on success + * false on failure + */ +int crypto_digest_update (DIGEST *digest, const void *data, size_t length) { + if (EVP_DigestUpdate(&digest->ctx, data, length) == 0) { + return true; + } else { + return false; + } +} + +/* + * Finalize the data in digest, storing the result in dest and the result size + * in length. The result size can be determined with crypto_digest_size(). + * + * Returns: true on success + * false on failure + */ +int crypto_digest_finalize (DIGEST *digest, void *dest, size_t *length) { + if (!EVP_DigestFinal(&digest->ctx, (unsigned char *) dest, length)) { + return false; + } else { + return true; + } +} + +/* + * Free memory associated with a digest object. + */ +void crypto_digest_free (DIGEST *digest) +{ + EVP_MD_CTX_cleanup(&digest->ctx); + free (digest); +} + +/* + * Create a new message signature context. + * Returns: A pointer to a SIGNATURE object on success. + * NULL on failure. + */ +SIGNATURE *crypto_sign_new (void) +{ + SIGNATURE *sig; + + sig = (SIGNATURE *) malloc(sizeof(SIGNATURE)); + if (!sig) { + return NULL; + } + + sig->sigData = SignatureData_new(); + + if (!sig->sigData) { + /* Allocation failed in OpenSSL */ + free(sig); + return NULL; + } + + /* Set the ASN.1 structure version number */ + ASN1_INTEGER_set(sig->sigData->version, BACULA_ASN1_VERSION); + + return sig; +} + +/* + * For a given public key, find the associated SignatureInfo record + * and create a digest context for signature validation + * Returns: CRYPTO_ERROR_NONE on success, with the newly allocated DIGEST in digest. + * A crypto_error_t value on failure. + */ +crypto_error_t crypto_sign_get_digest(SIGNATURE *sig, X509_KEYPAIR *keypair, DIGEST **digest) +{ + STACK_OF(SignerInfo) *signers; + SignerInfo *si; + int i; + + signers = sig->sigData->signerInfo; + + for (i = 0; i < sk_SignerInfo_num(signers); i++) { + si = sk_SignerInfo_value(signers, i); + if (M_ASN1_OCTET_STRING_cmp(keypair->keyid, si->subjectKeyIdentifier) == 0) { + /* Get the digest algorithm and allocate a digest context */ + switch (OBJ_obj2nid(si->digestAlgorithm)) { + case NID_md5: + *digest = crypto_digest_new(CRYPTO_DIGEST_MD5); + break; + case NID_sha1: + *digest = crypto_digest_new(CRYPTO_DIGEST_SHA1); + break; +#ifdef HAVE_SHA2 + case NID_sha256: + *digest = crypto_digest_new(CRYPTO_DIGEST_SHA256); + break; + case NID_sha512: + *digest = crypto_digest_new(CRYPTO_DIGEST_SHA512); + break; +#endif + default: + *digest = NULL; + return CRYPTO_ERROR_INVALID_DIGEST; + } + + /* Shouldn't happen */ + if (*digest == NULL) { + return CRYPTO_ERROR_INVALID_DIGEST; + } else { + return CRYPTO_ERROR_NONE; + } + } + } + + return CRYPTO_ERROR_NOSIGNER; +} + +/* + * For a given signature, public key, and digest, verify the SIGNATURE. + * Returns: CRYPTO_ERROR_NONE on success. + * A crypto_error_t value on failure. + */ +crypto_error_t crypto_sign_verify(SIGNATURE *sig, X509_KEYPAIR *keypair, DIGEST *digest) +{ + STACK_OF(SignerInfo) *signers; + SignerInfo *si; + int ok, i; + unsigned int sigLen; +#if (OPENSSL_VERSION_NUMBER >= 0x0090800FL) + const unsigned char *sigData; +#else + unsigned char *sigData; +#endif + + signers = sig->sigData->signerInfo; + + /* Find the signer */ + for (i = 0; i < sk_SignerInfo_num(signers); i++) { + si = sk_SignerInfo_value(signers, i); + if (M_ASN1_OCTET_STRING_cmp(keypair->keyid, si->subjectKeyIdentifier) == 0) { + /* Extract the signature data */ + sigLen = M_ASN1_STRING_length(si->signature); + sigData = M_ASN1_STRING_data(si->signature); + + ok = EVP_VerifyFinal(&digest->ctx, sigData, sigLen, keypair->pubkey); + if (ok >= 1) { + return CRYPTO_ERROR_NONE; + } else if (ok == 0) { + return CRYPTO_ERROR_BAD_SIGNATURE; + } else if (ok < 0) { + /* Shouldn't happen */ + openssl_post_errors(M_ERROR, _("OpenSSL error occured")); + return CRYPTO_ERROR_INTERNAL; + } + } + } + + /* Signer wasn't found. */ + return CRYPTO_ERROR_NOSIGNER; +} + + +/* + * Add a new signer + * Returns: true on success + * false on failure + */ +int crypto_sign_add_signer(SIGNATURE *sig, DIGEST *digest, X509_KEYPAIR *keypair) +{ + SignerInfo *si = NULL; + unsigned char *buf = NULL; + unsigned int len; + + si = SignerInfo_new(); + + if (!si) { + /* Allocation failed in OpenSSL */ + return false; + } + + /* Set the ASN.1 structure version number */ + ASN1_INTEGER_set(si->version, BACULA_ASN1_VERSION); + + /* Set the digest algorithm identifier */ + switch (digest->type) { + case CRYPTO_DIGEST_MD5: + si->digestAlgorithm = OBJ_nid2obj(NID_md5); + break; + case CRYPTO_DIGEST_SHA1: + si->digestAlgorithm = OBJ_nid2obj(NID_sha1); + break; +#ifdef HAVE_SHA2 + case CRYPTO_DIGEST_SHA256: + si->digestAlgorithm = OBJ_nid2obj(NID_sha256); + break; + case CRYPTO_DIGEST_SHA512: + si->digestAlgorithm = OBJ_nid2obj(NID_sha512); + break; +#endif + default: + /* This should never happen */ + goto err; + } + + /* Drop the string allocated by OpenSSL, and add our subjectKeyIdentifier */ + M_ASN1_OCTET_STRING_free(si->subjectKeyIdentifier); + si->subjectKeyIdentifier = M_ASN1_OCTET_STRING_dup(keypair->keyid); + + /* Set our signature algorithm. We currently require RSA */ + assert(EVP_PKEY_type(keypair->pubkey->type) == EVP_PKEY_RSA); + /* This is slightly evil. Reach into the MD structure and grab the key type */ + si->signatureAlgorithm = OBJ_nid2obj(digest->ctx.digest->pkey_type); + + /* Finalize/Sign our Digest */ + len = EVP_PKEY_size(keypair->privkey); + buf = (unsigned char *) malloc(len); + if (!EVP_SignFinal(&digest->ctx, buf, &len, keypair->privkey)) { + openssl_post_errors(M_ERROR, _("Signature creation failed")); + goto err; + } + + /* Add the signature to the SignerInfo structure */ + if (!M_ASN1_OCTET_STRING_set(si->signature, buf, len)) { + /* Allocation failed in OpenSSL */ + goto err; + } + + /* No longer needed */ + free(buf); + + /* Push the new SignerInfo structure onto the stack */ + sk_SignerInfo_push(sig->sigData->signerInfo, si); + + return true; + +err: + if (si) { + SignerInfo_free(si); + } + if (buf) { + free(buf); + } + + return false; +} + +/* + * Encodes the SignatureData structure. The length argument is used to specify the + * size of dest. A length of 0 will cause no data to be written to dest, and the + * required length to be written to length. The caller can then allocate sufficient + * space for the output. + * + * Returns: true on success, stores the encoded data in dest, and the size in length. + * false on failure. + */ +int crypto_sign_encode(SIGNATURE *sig, void *dest, size_t *length) +{ + if (*length == 0) { + *length = i2d_SignatureData(sig->sigData, NULL); + return true; + } + + *length = i2d_SignatureData(sig->sigData, (unsigned char **) &dest); + return true; +} + +/* + * Decodes the SignatureData structure. The length argument is used to specify the + * size of sigData. + * + * Returns: SIGNATURE instance on success. + * NULL on failure. + + */ + +SIGNATURE *crypto_sign_decode(const void *sigData, size_t length) +{ + SIGNATURE *sig; +#if (OPENSSL_VERSION_NUMBER >= 0x0090800FL) + const unsigned char *p = (const unsigned char *) sigData; +#else + unsigned char *p = (unsigned char *) sigData; +#endif + + sig = (SIGNATURE *) malloc(sizeof(SIGNATURE)); + if (!sig) { + return NULL; + } + + /* d2i_SignatureData modifies the supplied pointer */ + sig->sigData = d2i_SignatureData(NULL, &p, length); + + if (!sig->sigData) { + /* Allocation / Decoding failed in OpenSSL */ + openssl_post_errors(M_ERROR, _("Signature decoding failed")); + return NULL; + } + + return sig; +} + +/* + * Free memory associated with a signature object. + */ +void crypto_sign_free(SIGNATURE *sig) +{ + SignatureData_free(sig->sigData); + free (sig); +} + +/* + * Perform global initialization of OpenSSL + * This function is not thread safe. + * Returns: 0 on success + * errno on failure + */ +int init_crypto (void) +{ + int stat; + + if ((stat = openssl_init_threads()) != 0) { + Emsg1(M_ABORT, 0, _("Unable to init OpenSSL threading: ERR=%s\n"), strerror(stat)); + } + + /* Load libssl and libcrypto human-readable error strings */ + SSL_load_error_strings(); + + /* Register OpenSSL ciphers */ + SSL_library_init(); + + if (!openssl_seed_prng()) { + Emsg0(M_ERROR_TERM, 0, _("Failed to seed OpenSSL PRNG\n")); + } + + crypto_initialized = true; + + return stat; +} + +/* + * Perform global cleanup of OpenSSL + * All cryptographic operations must be completed before calling this function. + * This function is not thread safe. + * Returns: 0 on success + * errno on failure + */ +int cleanup_crypto (void) +{ + /* + * Ensure that we've actually been initialized; Doing this here decreases the + * complexity of client's termination/cleanup code. + */ + if (!crypto_initialized) { + return 0; + } + + if (!openssl_save_prng()) { + Emsg0(M_ERROR, 0, _("Failed to save OpenSSL PRNG\n")); + } + + openssl_cleanup_threads(); + + /* Free libssl and libcrypto error strings */ + ERR_free_strings(); + + /* Free memory used by PRNG */ + RAND_cleanup(); + + crypto_initialized = false; + + return 0; +} + + +#else /* HAVE_OPENSSL */ +# error No encryption library available +#endif /* HAVE_OPENSSL */ + +#else /* HAVE_CRYPTO */ + +/* + * Cryptography Support Disabled + */ + +/* Message Digest Structure */ +struct Digest { + crypto_digest_t type; + union { + SHA1Context sha1; + MD5Context md5; + }; +}; + +/* Dummy Signature Structure */ +struct Signature { +}; + +DIGEST *crypto_digest_new (crypto_digest_t type) +{ + DIGEST *digest; + + digest = (DIGEST *) malloc(sizeof(DIGEST)); + digest->type = type; + + switch (type) { + case CRYPTO_DIGEST_MD5: + MD5Init(&digest->md5); + break; + case CRYPTO_DIGEST_SHA1: + SHA1Init(&digest->sha1); + break; + default: + Emsg0(M_ERROR, 0, _("Unsupported digest type specified\n")); + free(digest); + return NULL; + } + + return (digest); +} + +int crypto_digest_update (DIGEST *digest, const void *data, size_t length) { + switch (digest->type) { + case CRYPTO_DIGEST_MD5: + /* Doesn't return anything ... */ + MD5Update(&digest->md5, (unsigned char *) data, length); + return true; + case CRYPTO_DIGEST_SHA1: + int ret; + if ((ret = SHA1Update(&digest->sha1, (const u_int8_t *) data, length)) == shaSuccess) { + return true; + } else { + Emsg1(M_ERROR, 0, _("SHA1Update() returned an error: %d\n"), ret); + return false; + } + break; + default: + return false; + } +} + +int crypto_digest_finalize (DIGEST *digest, void *dest, size_t *length) { + + switch (digest->type) { + case CRYPTO_DIGEST_MD5: + /* Guard against programmer error by either the API client or + * an out-of-sync CRYPTO_DIGEST_MAX_SIZE */ + assert(*length >= CRYPTO_DIGEST_MD5_SIZE); + *length = CRYPTO_DIGEST_MD5_SIZE; + /* Doesn't return anything ... */ + MD5Final((unsigned char *) dest, &digest->md5); + return true; + case CRYPTO_DIGEST_SHA1: + /* Guard against programmer error by either the API client or + * an out-of-sync CRYPTO_DIGEST_MAX_SIZE */ + assert(*length >= CRYPTO_DIGEST_SHA1_SIZE); + *length = CRYPTO_DIGEST_SHA1_SIZE; + if (SHA1Final(&digest->sha1, (u_int8_t *) dest) == shaSuccess) { + return true; + } else { + return false; + } + break; + default: + return false; + } + + return false; +} + +void crypto_digest_free (DIGEST *digest) +{ + free (digest); +} + +/* Dummy routines */ +int init_crypto (void) { return 0; } +int cleanup_crypto (void) { return 0; } + +SIGNATURE *crypto_sign_new (void) { return NULL; } + +crypto_error_t crypto_sign_get_digest (SIGNATURE *sig, X509_KEYPAIR *keypair, DIGEST **digest) { return CRYPTO_ERROR_INTERNAL; } +crypto_error_t crypto_sign_verify (SIGNATURE *sig, X509_KEYPAIR *keypair, DIGEST *digest) { return CRYPTO_ERROR_INTERNAL; } + +int crypto_sign_add_signer (SIGNATURE *sig, DIGEST *digest, X509_KEYPAIR *keypair) { return false; } +int crypto_sign_encode (SIGNATURE *sig, void *dest, size_t *length) { return false; } + +SIGNATURE *crypto_sign_decode (const void *sigData, size_t length) { return false; } +void crypto_sign_free (SIGNATURE *sig) { } + + +X509_KEYPAIR *crypto_keypair_new (void) { return NULL; } +int crypto_keypair_load_cert (X509_KEYPAIR *keypair, const char *file) { return false; } +int crypto_keypair_load_key (X509_KEYPAIR *keypair, const char *file, CRYPTO_PEM_PASSWD_CB *pem_callback, const void *pem_userdata) { return false; } +void crypto_keypair_free (X509_KEYPAIR *keypair) { } + +#endif /* HAVE_CRYPTO */ + +/* Shared Code */ + +/* + * Default PEM encryption passphrase callback. + * Returns an empty password. + */ +int crypto_default_pem_callback(char *buf, int size, const void *userdata) +{ + bstrncpy(buf, "", size); + return (strlen(buf)); +} + +/* + * Returns the ASCII name of the digest type. + * Returns: ASCII name of digest type. + */ +const char *crypto_digest_name (DIGEST *digest) { + switch (digest->type) { + case CRYPTO_DIGEST_MD5: + return "MD5"; + case CRYPTO_DIGEST_SHA1: + return "SHA1"; + case CRYPTO_DIGEST_SHA256: + return "SHA256"; + case CRYPTO_DIGEST_SHA512: + return "SHA512"; + case CRYPTO_DIGEST_NONE: + return "None"; + default: + return "Invalid Digest Type"; + } + +} + +/* + * Given a stream type, returns the associated + * crypto_digest_t value. + */ +crypto_digest_t crypto_digest_stream_type (int stream) { + switch (stream) { + case STREAM_MD5_DIGEST: + return CRYPTO_DIGEST_MD5; + case STREAM_SHA1_DIGEST: + return CRYPTO_DIGEST_SHA1; + case STREAM_SHA256_DIGEST: + return CRYPTO_DIGEST_SHA256; + case STREAM_SHA512_DIGEST: + return CRYPTO_DIGEST_SHA512; + default: + return CRYPTO_DIGEST_NONE; + } +} + +/* + * * Given a crypto_error_t value, return the associated + * * error string + * */ +const char *crypto_strerror(crypto_error_t error) { + switch (error) { + case CRYPTO_ERROR_NONE: + return "No error"; + case CRYPTO_ERROR_NOSIGNER: + return "Signer not found"; + case CRYPTO_ERROR_INVALID_DIGEST: + return "Unsupported digest algorithm"; + case CRYPTO_ERROR_BAD_SIGNATURE: + return "Signature is invalid"; + case CRYPTO_ERROR_INTERNAL: + /* This shouldn't happen */ + return "Internal error"; + default: + return "Unknown error"; + } +} diff --git a/bacula/src/lib/crypto.h b/bacula/src/lib/crypto.h new file mode 100644 index 0000000000..1519cf426c --- /dev/null +++ b/bacula/src/lib/crypto.h @@ -0,0 +1,99 @@ +/* + * crypto.h Encryption support functions + * + * Author: Landon Fuller + * + * Version $Id$ + * + * Copyright (C) 2005 Kern Sibbald + * + * This file was contributed to the Bacula project by Landon Fuller. + * + * Landon Fuller has been granted a perpetual, worldwide, non-exclusive, + * no-charge, royalty-free, irrevocable copyright * license to reproduce, + * prepare derivative works of, publicly display, publicly perform, + * sublicense, and distribute the original work contributed by Landon Fuller + * to the Bacula project in source or object form. + * + * If you wish to license these contributions under an alternate open source + * license please contact Landon Fuller . + */ +/* + Copyright (C) 2005 Kern Sibbald + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as amended with additional clauses defined in the + file LICENSE in the main source directory. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + the file LICENSE for additional details. + + */ + +#ifndef __CRYPTO_H_ +#define __CRYPTO_H_ + +/* Opaque X509 Public/Private Key Pair Structure */ +typedef struct X509_Keypair X509_KEYPAIR; + +/* Opaque Message Digest Structure */ +typedef struct Digest DIGEST; + +/* Opaque Message Signature Structure */ +typedef struct Signature SIGNATURE; + +/* PEM Decryption Passphrase Callback */ +typedef int (CRYPTO_PEM_PASSWD_CB) (char *buf, int size, const void *userdata); + +/* Digest Types */ +typedef enum { + /* These are stored on disk and MUST NOT change */ + CRYPTO_DIGEST_NONE = 0, + CRYPTO_DIGEST_MD5 = 1, + CRYPTO_DIGEST_SHA1 = 2, + CRYPTO_DIGEST_SHA256 = 3, + CRYPTO_DIGEST_SHA512 = 4 +} crypto_digest_t; + +/* Crypto API Errors */ +typedef enum { + CRYPTO_ERROR_NONE = 0, /* No error */ + CRYPTO_ERROR_NOSIGNER = 1, /* Signer not found */ + CRYPTO_ERROR_INVALID_DIGEST = 2, /* Unsupported digest algorithm */ + CRYPTO_ERROR_BAD_SIGNATURE = 3, /* Signature is invalid */ + CRYPTO_ERROR_INTERNAL = 4 /* Internal Error */ +} crypto_error_t; + +/* Message Digest Sizes */ +#define CRYPTO_DIGEST_MD5_SIZE 16 /* 128 bits */ +#define CRYPTO_DIGEST_SHA1_SIZE 20 /* 160 bits */ +#define CRYPTO_DIGEST_SHA256_SIZE 32 /* 256 bits */ +#define CRYPTO_DIGEST_SHA512_SIZE 64 /* 512 bits */ + +/* Maximum Message Digest Size */ +#ifdef HAVE_OPENSSL + +/* Let OpenSSL define it */ +#define CRYPTO_DIGEST_MAX_SIZE EVP_MAX_MD_SIZE + +#else /* HAVE_OPENSSL */ + +/* + * This must be kept in sync with the available message digest algorithms. + * Just in case someone forgets, I've added assertions + * to crypto_digest_finalize(). + * MD5: 128 bits + * SHA-1: 160 bits + */ +#ifndef HAVE_SHA2 +#define CRYPTO_DIGEST_MAX_SIZE CRYPTO_DIGEST_SHA1_SIZE +#else +#define CRYPTO_DIGEST_MAX_SIZE CRYPTO_DIGEST_SHA512_SIZE +#endif + +#endif /* HAVE_OPENSSL */ + +#endif /* __CRYPTO_H_ */ diff --git a/bacula/src/lib/hmac.c b/bacula/src/lib/hmac.c index bfe0bc9d1e..7dade4c136 100644 --- a/bacula/src/lib/hmac.c +++ b/bacula/src/lib/hmac.c @@ -29,8 +29,8 @@ #include "bacula.h" -#define PAD_LEN 64 /* PAD length */ -#define SIG_LEN 16 /* MD5 signature length */ +#define PAD_LEN 64 /* PAD length */ +#define SIG_LEN MD5HashSize /* MD5 digest length */ void hmac_md5( diff --git a/bacula/src/lib/lib.h b/bacula/src/lib/lib.h index 7d7df09f14..5b5b6a82e1 100644 --- a/bacula/src/lib/lib.h +++ b/bacula/src/lib/lib.h @@ -31,10 +31,13 @@ #include "smartall.h" #include "alist.h" #include "dlist.h" +#include "base64.h" #include "bits.h" #include "btime.h" +#include "crypto.h" #include "mem_pool.h" #include "message.h" +#include "openssl.h" #include "lex.h" #include "parse_conf.h" #include "tls.h" diff --git a/bacula/src/lib/md5.h b/bacula/src/lib/md5.h index a927064be5..f97e00d403 100644 --- a/bacula/src/lib/md5.h +++ b/bacula/src/lib/md5.h @@ -28,6 +28,8 @@ #ifndef __BMD5_H #define __BMD5_H +#define MD5HashSize 16 + struct MD5Context { uint32_t buf[4]; uint32_t bits[2]; diff --git a/bacula/src/lib/openssl.c b/bacula/src/lib/openssl.c new file mode 100644 index 0000000000..929dee7c26 --- /dev/null +++ b/bacula/src/lib/openssl.c @@ -0,0 +1,232 @@ +/* + * openssl.c OpenSSL support functions + * + * Author: Landon Fuller + * + * Version $Id$ + * + * Copyright (C) 2005 Kern Sibbald + * + * This file was contributed to the Bacula project by Landon Fuller. + * + * Landon Fuller has been granted a perpetual, worldwide, non-exclusive, + * no-charge, royalty-free, irrevocable copyright license to reproduce, + * prepare derivative works of, publicly display, publicly perform, + * sublicense, and distribute the original work contributed by Landon Fuller + * to the Bacula project in source or object form. + * + * If you wish to license these contributions under an alternate open source + * license please contact Landon Fuller . + */ +/* + Copyright (C) 2005 Kern Sibbald + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as amended with additional clauses defined in the + file LICENSE in the main source directory. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + the file LICENSE for additional details. + + */ + + +#include "bacula.h" +#include + +#ifdef HAVE_OPENSSL + +/* Array of mutexes for use with OpenSSL static locking */ +static pthread_mutex_t *mutexes; + +/* OpenSSL dynamic locking structure */ +struct CRYPTO_dynlock_value { + pthread_mutex_t mutex; +}; + + +/* + * Post all per-thread openssl errors + */ +void openssl_post_errors(int code, const char *errstring) +{ + char buf[512]; + unsigned long sslerr; + + /* Pop errors off of the per-thread queue */ + while((sslerr = ERR_get_error()) != 0) { + /* Acquire the human readable string */ + ERR_error_string_n(sslerr, (char *) &buf, sizeof(buf)); + Emsg2(M_ERROR, 0, "%s: ERR=%s\n", errstring, buf); + } +} + +/* + * Return an OpenSSL thread ID + * Returns: thread ID + * + */ +static unsigned long get_openssl_thread_id (void) +{ + /* Comparison without use of pthread_equal() is mandated by the OpenSSL API */ + return ((unsigned long) pthread_self()); +} + +/* + * Allocate a dynamic OpenSSL mutex + */ +static struct CRYPTO_dynlock_value *openssl_create_dynamic_mutex (const char *file, int line) +{ + struct CRYPTO_dynlock_value *dynlock; + int stat; + + dynlock = (struct CRYPTO_dynlock_value *) malloc(sizeof(struct CRYPTO_dynlock_value)); + + if ((stat = pthread_mutex_init(&dynlock->mutex, NULL)) != 0) { + Emsg1(M_ABORT, 0, _("Unable to init mutex: ERR=%s\n"), strerror(stat)); + } + + return dynlock; +} + +static void openssl_update_dynamic_mutex (int mode, struct CRYPTO_dynlock_value *dynlock, const char *file, int line) +{ + if (mode & CRYPTO_LOCK) { + P(dynlock->mutex); + } else { + V(dynlock->mutex); + } +} + +static void openssl_destroy_dynamic_mutex (struct CRYPTO_dynlock_value *dynlock, const char *file, int line) +{ + int stat; + + if ((stat = pthread_mutex_destroy(&dynlock->mutex)) != 0) { + Emsg1(M_ABORT, 0, _("Unable to destroy mutex: ERR=%s\n"), strerror(stat)); + } + + free(dynlock); +} + +/* + * (Un)Lock a static OpenSSL mutex + */ +static void openssl_update_static_mutex (int mode, int i, const char *file, int line) +{ + if (mode & CRYPTO_LOCK) { + P(mutexes[i]); + } else { + V(mutexes[i]); + } +} + +/* + * Initialize OpenSSL thread support + * Returns: 0 on success + * errno on failure + */ +int openssl_init_threads (void) +{ + int i, numlocks; + int stat; + + + /* Set thread ID callback */ + CRYPTO_set_id_callback(get_openssl_thread_id); + + /* Initialize static locking */ + numlocks = CRYPTO_num_locks(); + mutexes = (pthread_mutex_t *) malloc(numlocks * sizeof(pthread_mutex_t)); + for (i = 0; i < numlocks; i++) { + if ((stat = pthread_mutex_init(&mutexes[i], NULL)) != 0) { + Emsg1(M_ERROR, 0, _("Unable to init mutex: ERR=%s\n"), strerror(stat)); + return stat; + } + } + + /* Set static locking callback */ + CRYPTO_set_locking_callback(openssl_update_static_mutex); + + /* Initialize dyanmic locking */ + CRYPTO_set_dynlock_create_callback(openssl_create_dynamic_mutex); + CRYPTO_set_dynlock_lock_callback(openssl_update_dynamic_mutex); + CRYPTO_set_dynlock_destroy_callback(openssl_destroy_dynamic_mutex); + + return 0; +} + +/* + * Clean up OpenSSL threading support + */ +void openssl_cleanup_threads (void) +{ + int i, numlocks; + int stat; + + /* Unset thread ID callback */ + CRYPTO_set_id_callback(NULL); + + /* Deallocate static lock mutexes */ + numlocks = CRYPTO_num_locks(); + for (i = 0; i < numlocks; i++) { + if ((stat = pthread_mutex_destroy(&mutexes[i])) != 0) { + /* We don't halt execution, reporting the error should be sufficient */ + Emsg1(M_ERROR, 0, _("Unable to destroy mutex: ERR=%s\n"), strerror(stat)); + } + } + + /* Unset static locking callback */ + CRYPTO_set_locking_callback(NULL); + + /* Free static lock array */ + free(mutexes); + + /* Unset dynamic locking callbacks */ + CRYPTO_set_dynlock_create_callback(NULL); + CRYPTO_set_dynlock_lock_callback(NULL); + CRYPTO_set_dynlock_destroy_callback(NULL); +} + + +/* + * Seed OpenSSL PRNG + * Returns: 1 on success + * 0 on failure + */ +int openssl_seed_prng (void) +{ + const char *names[] = { "/dev/urandom", "/dev/random", NULL }; + int i; + + // ***FIXME*** + // Win32 Support + // Read saved entropy? + + for (i = 0; names[i]; i++) { + if (RAND_load_file(names[i], 1024) != -1) { + /* Success */ + return 1; + } + } + + /* Fail */ + return 0; +} + +/* + * Save OpenSSL Entropy + * Returns: 1 on success + * 0 on failure + */ +int openssl_save_prng (void) +{ + // ***FIXME*** + // Implement PRNG state save + return 1; +} + +#endif /* HAVE_OPENSSL */ diff --git a/bacula/src/lib/openssl.h b/bacula/src/lib/openssl.h new file mode 100644 index 0000000000..7654e177cf --- /dev/null +++ b/bacula/src/lib/openssl.h @@ -0,0 +1,47 @@ +/* + * openssl.h OpenSSL support functions + * + * Author: Landon Fuller + * + * Version $Id$ + * + * Copyright (C) 2005 Kern Sibbald + * + * This file was contributed to the Bacula project by Landon Fuller. + * + * Landon Fuller has been granted a perpetual, worldwide, non-exclusive, + * no-charge, royalty-free, irrevocable copyright * license to reproduce, + * prepare derivative works of, publicly display, publicly perform, + * sublicense, and distribute the original work contributed by Landon Fuller + * to the Bacula project in source or object form. + * + * If you wish to license these contributions under an alternate open source + * license please contact Landon Fuller . + */ +/* + Copyright (C) 2005 Kern Sibbald + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as amended with additional clauses defined in the + file LICENSE in the main source directory. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + the file LICENSE for additional details. + + */ + +#ifndef __OPENSSL_H_ +#define __OPENSSL_H_ + +#ifdef HAVE_OPENSSL +void openssl_post_errors (int code, const char *errstring); +int openssl_init_threads (void); +void openssl_cleanup_threads (void); +int openssl_seed_prng (void); +int openssl_save_prng (void); +#endif /* HAVE_OPENSSL */ + +#endif /* __OPENSSL_H_ */ diff --git a/bacula/src/lib/parse_conf.c b/bacula/src/lib/parse_conf.c index 18a103a65e..41307bd07b 100755 --- a/bacula/src/lib/parse_conf.c +++ b/bacula/src/lib/parse_conf.c @@ -423,7 +423,7 @@ void store_password(LEX *lc, RES_ITEM *item, int index, int pass) { unsigned int i, j; struct MD5Context md5c; - unsigned char signature[16]; + unsigned char digest[CRYPTO_DIGEST_MD5_SIZE]; char sig[100]; @@ -431,9 +431,9 @@ void store_password(LEX *lc, RES_ITEM *item, int index, int pass) if (pass == 1) { MD5Init(&md5c); MD5Update(&md5c, (unsigned char *) (lc->str), lc->str_len); - MD5Final(signature, &md5c); - for (i = j = 0; i < sizeof(signature); i++) { - sprintf(&sig[j], "%02x", signature[i]); + MD5Final(digest, &md5c); + for (i = j = 0; i < sizeof(digest); i++) { + sprintf(&sig[j], "%02x", digest[i]); j += 2; } *(item->value) = bstrdup(sig); diff --git a/bacula/src/lib/protos.h b/bacula/src/lib/protos.h index 61c01a7cab..244f1ca49c 100644 --- a/bacula/src/lib/protos.h +++ b/bacula/src/lib/protos.h @@ -113,6 +113,29 @@ void hmac_md5(uint8_t* text, int text_len, uint8_t* key, uint32_t bcrc32(uint8_t *buf, int len); +/* crypto.c */ +int init_crypto (void); +int cleanup_crypto (void); +DIGEST * crypto_digest_new (crypto_digest_t type); +int crypto_digest_update (DIGEST *digest, const void *data, size_t length); +int crypto_digest_finalize (DIGEST *digest, void *dest, size_t *length); +void crypto_digest_free (DIGEST *digest); +SIGNATURE * crypto_sign_new (void); +crypto_error_t crypto_sign_get_digest (SIGNATURE *sig, X509_KEYPAIR *keypair, DIGEST **digest); +crypto_error_t crypto_sign_verify (SIGNATURE *sig, X509_KEYPAIR *keypair, DIGEST *digest); +int crypto_sign_add_signer (SIGNATURE *sig, DIGEST *digest, X509_KEYPAIR *keypair); +int crypto_sign_encode (SIGNATURE *sig, void *dest, size_t *length); +SIGNATURE * crypto_sign_decode (const void *sigData, size_t length); +void crypto_sign_free (SIGNATURE *sig); +X509_KEYPAIR * crypto_keypair_new (void); +int crypto_keypair_load_cert (X509_KEYPAIR *keypair, const char *file); +int crypto_keypair_load_key (X509_KEYPAIR *keypair, const char *file, CRYPTO_PEM_PASSWD_CB *pem_callback, const void *pem_userdata); +void crypto_keypair_free (X509_KEYPAIR *keypair); +int crypto_default_pem_callback (char *buf, int size, const void *userdata); +const char * crypto_digest_name (DIGEST *digest); +crypto_digest_t crypto_digest_stream_type (int stream); +const char * crypto_strerror (crypto_error_t error); + /* daemon.c */ void daemon_start (); @@ -209,14 +232,11 @@ int bsscanf(const char *buf, const char *fmt, ...); /* tls.c */ -int init_tls (void); -int cleanup_tls (void); - TLS_CONTEXT *new_tls_context (const char *ca_certfile, const char *ca_certdir, const char *certfile, const char *keyfile, - TLS_PEM_PASSWD_CB *pem_callback, + CRYPTO_PEM_PASSWD_CB *pem_callback, const void *pem_userdata, const char *dhfile, bool verify_peer); diff --git a/bacula/src/lib/tls.c b/bacula/src/lib/tls.c index c660505f3c..d9d3b51002 100644 --- a/bacula/src/lib/tls.c +++ b/bacula/src/lib/tls.c @@ -36,6 +36,7 @@ */ + #include "bacula.h" #include @@ -48,21 +49,10 @@ extern time_t watchdog_time; /* No anonymous ciphers, no <128 bit ciphers, no export ciphers, no MD5 ciphers */ #define TLS_DEFAULT_CIPHERS "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH" -/* Array of mutexes for use with OpenSSL static locking */ -static pthread_mutex_t *mutexes; - -/* OpenSSL dynamic locking structure */ -struct CRYPTO_dynlock_value { - pthread_mutex_t mutex; -}; - -/* Are we initialized? */ -static int tls_initialized = false; - /* TLS Context Structure */ struct TLS_Context { SSL_CTX *openssl; - TLS_PEM_PASSWD_CB *pem_callback; + CRYPTO_PEM_PASSWD_CB *pem_callback; const void *pem_userdata; }; @@ -70,20 +60,6 @@ struct TLS_Connection { SSL *openssl; }; -/* post all per-thread openssl errors */ -static void openssl_post_errors(int code, const char *errstring) -{ - char buf[512]; - unsigned long sslerr; - - /* Pop errors off of the per-thread queue */ - while((sslerr = ERR_get_error()) != 0) { - /* Acquire the human readable string */ - ERR_error_string_n(sslerr, (char *) &buf, sizeof(buf)); - Emsg2(M_ERROR, 0, "%s: ERR=%s\n", errstring, buf); - } -} - /* * OpenSSL certificate verification callback. * OpenSSL has already performed internal certificate verification. @@ -111,18 +87,8 @@ static int openssl_verify_peer(int ok, X509_STORE_CTX *store) return ok; } -/* - * Default PEM encryption passphrase callback. - * Returns an empty password. - */ -static int tls_default_pem_callback(char *buf, int size, const void *userdata) -{ - bstrncpy(buf, "", size); - return (strlen(buf)); -} - /* Dispatch user PEM encryption callbacks */ -static int openssl_pem_callback_dispatch (char *buf, int size, int rwflag, void *userdata) +static int tls_pem_callback_dispatch (char *buf, int size, int rwflag, void *userdata) { TLS_CONTEXT *ctx = (TLS_CONTEXT *) userdata; return (ctx->pem_callback(buf, size, ctx->pem_userdata)); @@ -135,7 +101,7 @@ static int openssl_pem_callback_dispatch (char *buf, int size, int rwflag, void */ TLS_CONTEXT *new_tls_context(const char *ca_certfile, const char *ca_certdir, const char *certfile, const char *keyfile, - TLS_PEM_PASSWD_CB *pem_callback, + CRYPTO_PEM_PASSWD_CB *pem_callback, const void *pem_userdata, const char *dhfile, bool verify_peer) { @@ -158,10 +124,10 @@ TLS_CONTEXT *new_tls_context(const char *ca_certfile, const char *ca_certdir, ctx->pem_callback = pem_callback; ctx->pem_userdata = pem_userdata; } else { - ctx->pem_callback = tls_default_pem_callback; + ctx->pem_callback = crypto_default_pem_callback; ctx->pem_userdata = NULL; } - SSL_CTX_set_default_passwd_cb(ctx->openssl, openssl_pem_callback_dispatch); + SSL_CTX_set_default_passwd_cb(ctx->openssl, tls_pem_callback_dispatch); SSL_CTX_set_default_passwd_cb_userdata(ctx->openssl, (void *) ctx); /* @@ -367,7 +333,7 @@ bool tls_postconnect_verify_host(TLS_CONNECTION *tls, const char *host) for (j = 0; j < sk_CONF_VALUE_num(val); j++) { nval = sk_CONF_VALUE_value(val, j); if (strcmp(nval->name, "DNS") == 0) { - if (strcasecmp(nval->name, host) == 0) { + if (strcasecmp(nval->value, host) == 0) { auth_success = true; goto success; } @@ -692,234 +658,6 @@ int tls_bsock_readn(BSOCK *bsock, char *ptr, int32_t nbytes) { return (openssl_bsock_readwrite(bsock, ptr, nbytes, false)); } -/* - * Return an OpenSSL thread ID - * Returns: thread ID - * - */ -static unsigned long get_openssl_thread_id (void) -{ - /* Comparison without use of pthread_equal() is mandated by the OpenSSL API */ - return ((unsigned long) pthread_self()); -} - -/* - * Allocate a dynamic OpenSSL mutex - */ -static struct CRYPTO_dynlock_value *openssl_create_dynamic_mutex (const char *file, int line) -{ - struct CRYPTO_dynlock_value *dynlock; - int stat; - - dynlock = (struct CRYPTO_dynlock_value *) malloc(sizeof(struct CRYPTO_dynlock_value)); - - if ((stat = pthread_mutex_init(&dynlock->mutex, NULL)) != 0) { - Emsg1(M_ABORT, 0, _("Unable to init mutex: ERR=%s\n"), strerror(stat)); - } - - return dynlock; -} - -static void openssl_update_dynamic_mutex (int mode, struct CRYPTO_dynlock_value *dynlock, const char *file, int line) -{ - if (mode & CRYPTO_LOCK) { - P(dynlock->mutex); - } else { - V(dynlock->mutex); - } -} - -static void openssl_destroy_dynamic_mutex (struct CRYPTO_dynlock_value *dynlock, const char *file, int line) -{ - int stat; - - if ((stat = pthread_mutex_destroy(&dynlock->mutex)) != 0) { - Emsg1(M_ABORT, 0, _("Unable to destroy mutex: ERR=%s\n"), strerror(stat)); - } - - free(dynlock); -} - -/* - * (Un)Lock a static OpenSSL mutex - */ -static void openssl_update_static_mutex (int mode, int i, const char *file, int line) -{ - if (mode & CRYPTO_LOCK) { - P(mutexes[i]); - } else { - V(mutexes[i]); - } -} - -/* - * Initialize OpenSSL thread support - * Returns: 0 on success - * errno on failure - */ -static int openssl_init_threads (void) -{ - int i, numlocks; - int stat; - - - /* Set thread ID callback */ - CRYPTO_set_id_callback(get_openssl_thread_id); - - /* Initialize static locking */ - numlocks = CRYPTO_num_locks(); - mutexes = (pthread_mutex_t *) malloc(numlocks * sizeof(pthread_mutex_t)); - for (i = 0; i < numlocks; i++) { - if ((stat = pthread_mutex_init(&mutexes[i], NULL)) != 0) { - Emsg1(M_ERROR, 0, _("Unable to init mutex: ERR=%s\n"), strerror(stat)); - return stat; - } - } - - /* Set static locking callback */ - CRYPTO_set_locking_callback(openssl_update_static_mutex); - - /* Initialize dyanmic locking */ - CRYPTO_set_dynlock_create_callback(openssl_create_dynamic_mutex); - CRYPTO_set_dynlock_lock_callback(openssl_update_dynamic_mutex); - CRYPTO_set_dynlock_destroy_callback(openssl_destroy_dynamic_mutex); - - return 0; -} - -/* - * Clean up OpenSSL threading support - */ -static void openssl_cleanup_threads (void) -{ - int i, numlocks; - int stat; - - /* Unset thread ID callback */ - CRYPTO_set_id_callback(NULL); - - /* Deallocate static lock mutexes */ - numlocks = CRYPTO_num_locks(); - for (i = 0; i < numlocks; i++) { - if ((stat = pthread_mutex_destroy(&mutexes[i])) != 0) { - /* We don't halt execution, reporting the error should be sufficient */ - Emsg1(M_ERROR, 0, _("Unable to destroy mutex: ERR=%s\n"), strerror(stat)); - } - } - - /* Unset static locking callback */ - CRYPTO_set_locking_callback(NULL); - - /* Free static lock array */ - free(mutexes); - - /* Unset dynamic locking callbacks */ - CRYPTO_set_dynlock_create_callback(NULL); - CRYPTO_set_dynlock_lock_callback(NULL); - CRYPTO_set_dynlock_destroy_callback(NULL); -} - - -/* - * Seed TLS PRNG - * Returns: 1 on success - * 0 on failure - */ -static int seed_tls_prng (void) -{ - const char *names[] = { "/dev/urandom", "/dev/random", NULL }; - int i; - - // ***FIXME*** - // Win32 Support - // Read saved entropy? - - for (i = 0; names[i]; i++) { - if (RAND_load_file(names[i], 1024) != -1) { - /* Success */ - return 1; - } - } - - /* Fail */ - return 0; -} - -/* - * Save TLS Entropy - * Returns: 1 on success - * 0 on failure - */ -static int save_tls_prng (void) -{ - // ***FIXME*** - // Implement PRNG state save - return 1; -} - -/* - * Perform global initialization of TLS - * This function is not thread safe. - * Returns: 0 on success - * errno on failure - */ -int init_tls (void) -{ - int stat; - - if ((stat = openssl_init_threads()) != 0) { - Emsg1(M_ABORT, 0, _("Unable to init OpenSSL threading: ERR=%s\n"), strerror(stat)); - } - - /* Load libssl and libcrypto human-readable error strings */ - SSL_load_error_strings(); - - /* Register OpenSSL ciphers */ - SSL_library_init(); - - if (!seed_tls_prng()) { - Emsg0(M_ERROR_TERM, 0, _("Failed to seed OpenSSL PRNG\n")); - } - - tls_initialized = true; - - return stat; -} - -/* - * Perform global cleanup of TLS - * All TLS connections must be closed before calling this function. - * This function is not thread safe. - * Returns: 0 on success - * errno on failure - */ -int cleanup_tls (void) -{ - /* - * Ensure that we've actually been initialized; Doing this here decreases the - * complexity of client's termination/cleanup code. - */ - if (!tls_initialized) { - return 0; - } - - if (!save_tls_prng()) { - Emsg0(M_ERROR, 0, _("Failed to save OpenSSL PRNG\n")); - } - - openssl_cleanup_threads(); - - /* Free libssl and libcrypto error strings */ - ERR_free_strings(); - - /* Free memory used by PRNG */ - RAND_cleanup(); - - tls_initialized = false; - - return 0; -} - #else /* HAVE_OPENSSL */ # error No TLS implementation available. #endif /* !HAVE_OPENSSL */ @@ -927,11 +665,9 @@ int cleanup_tls (void) #else /* Dummy routines */ -int init_tls(void) { return 0; } -int cleanup_tls (void) { return 0; } TLS_CONTEXT *new_tls_context(const char *ca_certfile, const char *ca_certdir, const char *certfile, const char *keyfile, - TLS_PEM_PASSWD_CB *pem_callback, + CRYPTO_PEM_PASSWD_CB *pem_callback, const void *pem_userdata, const char *dhfile, bool verify_peer) { diff --git a/bacula/src/lib/tls.h b/bacula/src/lib/tls.h index 13595dd17f..28d6fd6111 100644 --- a/bacula/src/lib/tls.h +++ b/bacula/src/lib/tls.h @@ -50,7 +50,4 @@ typedef struct TLS_Context TLS_CONTEXT; /* Opaque TLS Connection Structure */ typedef struct TLS_Connection TLS_CONNECTION; -/* PEM Decryption Passphrase Callback */ -typedef int (TLS_PEM_PASSWD_CB) (char *buf, int size, const void *userdata); - #endif /* __TLS_H_ */ diff --git a/bacula/src/stored/append.c b/bacula/src/stored/append.c index be4d23a0e6..39ec6d3903 100644 --- a/bacula/src/stored/append.c +++ b/bacula/src/stored/append.c @@ -208,9 +208,9 @@ bool do_append_data(JCR *jcr) FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId, stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len); - /* Send attributes and MD5 to Director for Catalog */ - if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_MD5_SIGNATURE || - stream == STREAM_UNIX_ATTRIBUTES_EX || stream == STREAM_SHA1_SIGNATURE) { + /* Send attributes and digest to Director for Catalog */ + if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX || + crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) { if (!jcr->no_attributes) { if (are_attributes_spooled(jcr)) { jcr->dir_bsock->spool = true; diff --git a/bacula/src/stored/bextract.c b/bacula/src/stored/bextract.c index addb74bbdc..c7717a4b6c 100644 --- a/bacula/src/stored/bextract.c +++ b/bacula/src/stored/bextract.c @@ -425,8 +425,14 @@ static bool record_cb(DCR *dcr, DEV_RECORD *rec) #endif break; - case STREAM_MD5_SIGNATURE: - case STREAM_SHA1_SIGNATURE: + case STREAM_MD5_DIGEST: + case STREAM_SHA1_DIGEST: + case STREAM_SHA256_DIGEST: + case STREAM_SHA512_DIGEST: + break; + + case STREAM_SIGNED_DIGEST: + // TODO landonf: Investigate signed digest support in the storage daemon break; case STREAM_PROGRAM_NAMES: diff --git a/bacula/src/stored/bscan.c b/bacula/src/stored/bscan.c index bd9b310e92..de44728abc 100644 --- a/bacula/src/stored/bscan.c +++ b/bacula/src/stored/bscan.c @@ -48,7 +48,7 @@ static int create_client_record(B_DB *db, CLIENT_DBR *cr); static int create_fileset_record(B_DB *db, FILESET_DBR *fsr); static int create_jobmedia_record(B_DB *db, JCR *jcr); static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId); -static int update_SIG_record(B_DB *db, char *SIGbuf, DEV_RECORD *rec, int type); +static int update_digest_record(B_DB *db, char *digest, DEV_RECORD *rec, int type); /* Global variables */ @@ -372,6 +372,7 @@ static bool record_cb(DCR *dcr, DEV_RECORD *rec) DEVICE *dev = dcr->dev; JCR *bjcr = dcr->jcr; DEV_BLOCK *block = dcr->block; + char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)]; if (rec->data_len > 0) { mr.VolBytes += rec->data_len + WRITE_RECHDR_LENGTH; /* Accumulate Volume bytes */ @@ -699,24 +700,44 @@ static bool record_cb(DCR *dcr, DEV_RECORD *rec) free_jcr(mjcr); /* done using JCR */ break; - case STREAM_MD5_SIGNATURE: - char MD5buf[50]; - bin_to_base64(MD5buf, (char *)rec->data, 16); /* encode 16 bytes */ + case STREAM_MD5_DIGEST: + bin_to_base64(digest, (char *)rec->data, CRYPTO_DIGEST_MD5_SIZE); if (verbose > 1) { - Pmsg1(000, _("Got MD5 record: %s\n"), MD5buf); + Pmsg1(000, _("Got MD5 record: %s\n"), digest); } - update_SIG_record(db, MD5buf, rec, MD5_SIG); + update_digest_record(db, digest, rec, CRYPTO_DIGEST_MD5); break; - case STREAM_SHA1_SIGNATURE: - char SIGbuf[50]; - bin_to_base64(SIGbuf, (char *)rec->data, 20); /* encode 20 bytes */ + case STREAM_SHA1_DIGEST: + bin_to_base64(digest, (char *)rec->data, CRYPTO_DIGEST_SHA1_SIZE); if (verbose > 1) { - Pmsg1(000, _("Got SHA1 record: %s\n"), SIGbuf); + Pmsg1(000, _("Got SHA1 record: %s\n"), digest); } - update_SIG_record(db, SIGbuf, rec, SHA1_SIG); + update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA1); break; + case STREAM_SHA256_DIGEST: + bin_to_base64(digest, (char *)rec->data, CRYPTO_DIGEST_SHA256_SIZE); + if (verbose > 1) { + Pmsg1(000, _("Got SHA256 record: %s\n"), digest); + } + update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA256); + break; + + case STREAM_SHA512_DIGEST: + bin_to_base64(digest, (char *)rec->data, CRYPTO_DIGEST_SHA512_SIZE); + if (verbose > 1) { + Pmsg1(000, _("Got SHA512 record: %s\n"), digest); + } + update_digest_record(db, digest, rec, CRYPTO_DIGEST_SHA512); + break; + + case STREAM_SIGNED_DIGEST: + // TODO landonf: Investigate signed digest support in bscan + if (verbose > 1) { + Pmsg0(000, _("Got signed digest record\n")); + } + break; case STREAM_PROGRAM_NAMES: if (verbose) { @@ -1148,7 +1169,7 @@ static int create_jobmedia_record(B_DB *db, JCR *mjcr) /* * Simulate the database call that updates the MD5/SHA1 record */ -static int update_SIG_record(B_DB *db, char *SIGbuf, DEV_RECORD *rec, int type) +static int update_digest_record(B_DB *db, char *digest, DEV_RECORD *rec, int type) { JCR *mjcr; @@ -1168,7 +1189,7 @@ static int update_SIG_record(B_DB *db, char *SIGbuf, DEV_RECORD *rec, int type) return 1; } - if (!db_add_SIG_to_file_record(bjcr, db, mjcr->FileId, SIGbuf, type)) { + if (!db_add_digest_to_file_record(bjcr, db, mjcr->FileId, digest, type)) { Pmsg1(0, _("Could not add MD5/SHA1 to File record. ERR=%s\n"), db_strerror(db)); free_jcr(mjcr); return 0; diff --git a/bacula/src/stored/read_record.c b/bacula/src/stored/read_record.c index c17be35dce..3f8f9a386b 100644 --- a/bacula/src/stored/read_record.c +++ b/bacula/src/stored/read_record.c @@ -222,7 +222,7 @@ bool read_records(DCR *dcr, break; /* read second part of record */ } ok = record_cb(dcr, rec); - if (rec->Stream == STREAM_MD5_SIGNATURE || rec->Stream == STREAM_SHA1_SIGNATURE) { + if (crypto_digest_stream_type(rec->Stream) != CRYPTO_DIGEST_NONE) { Dmsg3(300, "Done FI=%u before set_eof pos %u:%u\n", rec->FileIndex, dev->file, dev->block_num); if (match_set_eof(jcr->bsr, rec) && try_repositioning(jcr, rec, dev)) { diff --git a/bacula/src/stored/record.c b/bacula/src/stored/record.c index 3228fa7331..522c50a49b 100644 --- a/bacula/src/stored/record.c +++ b/bacula/src/stored/record.c @@ -89,9 +89,9 @@ const char *stream_to_ascii(char *buf, int stream, int fi) return "WIN32-DATA"; case STREAM_WIN32_GZIP_DATA: return "WIN32-GZIP"; - case STREAM_MD5_SIGNATURE: + case STREAM_MD5_DIGEST: return "MD5"; - case STREAM_SHA1_SIGNATURE: + case STREAM_SHA1_DIGEST: return "SHA1"; case STREAM_GZIP_DATA: return "GZIP"; @@ -109,6 +109,12 @@ const char *stream_to_ascii(char *buf, int stream, int fi) return "MACOS-RSRC"; case STREAM_HFSPLUS_ATTRIBUTES: return "HFSPLUS-ATTR"; + case STREAM_SHA256_DIGEST: + return "SHA256"; + case STREAM_SHA512_DIGEST: + return "SHA512"; + case STREAM_SIGNED_DIGEST: + return "SIGNED-DIGEST"; case -STREAM_UNIX_ATTRIBUTES: return "contUATTR"; case -STREAM_FILE_DATA: @@ -117,9 +123,9 @@ const char *stream_to_ascii(char *buf, int stream, int fi) return "contWIN32-DATA"; case -STREAM_WIN32_GZIP_DATA: return "contWIN32-GZIP"; - case -STREAM_MD5_SIGNATURE: + case -STREAM_MD5_DIGEST: return "contMD5"; - case -STREAM_SHA1_SIGNATURE: + case -STREAM_SHA1_DIGEST: return "contSHA1"; case -STREAM_GZIP_DATA: return "contGZIP"; @@ -137,6 +143,12 @@ const char *stream_to_ascii(char *buf, int stream, int fi) return "contMACOS-RSRC"; case -STREAM_HFSPLUS_ATTRIBUTES: return "contHFSPLUS-ATTR"; + case -STREAM_SHA256_DIGEST: + return "contSHA256"; + case -STREAM_SHA512_DIGEST: + return "contSHA512"; + case -STREAM_SIGNED_DIGEST: + return "contSIGNED-DIGEST"; default: sprintf(buf, "%d", stream); return buf; diff --git a/bacula/src/stored/stored.c b/bacula/src/stored/stored.c index 9b0ab92f4f..01561aa46c 100644 --- a/bacula/src/stored/stored.c +++ b/bacula/src/stored/stored.c @@ -185,8 +185,8 @@ int main (int argc, char *argv[]) parse_config(configfile); - if (init_tls() != 0) { - Jmsg((JCR *)NULL, M_ERROR_TERM, 0, _("TLS library initialization failed.\n")); + if (init_crypto() != 0) { + Jmsg((JCR *)NULL, M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n")); } if (!check_resources()) { @@ -565,7 +565,7 @@ void terminate_stored(int sig) } term_msg(); stop_watchdog(); - cleanup_tls(); + cleanup_crypto(); free_volume_list(); close_memory_pool(); diff --git a/bacula/src/wx-console/console_thread.cpp b/bacula/src/wx-console/console_thread.cpp index 3f2e908420..0f98b5a01b 100644 --- a/bacula/src/wx-console/console_thread.cpp +++ b/bacula/src/wx-console/console_thread.cpp @@ -223,8 +223,8 @@ wxString console_thread::LoadConfig(wxString configfile) { return errmsg; } - if (init_tls() != 0) { - Jmsg(NULL, M_ERROR_TERM, 0, wxString(_("TLS library initialization failed.\n")).mb_str(*wxConvCurrent)); + if (init_crypto() != 0) { + Jmsg(NULL, M_ERROR_TERM, 0, wxString(_("Cryptographic library initialization failed.\n")).mb_str(*wxConvCurrent)); } if (!check_resources()) {