diff --git a/sbin/geom/class/eli/geli.8 b/sbin/geom/class/eli/geli.8 index d465099295d5..ef9da572cc70 100644 --- a/sbin/geom/class/eli/geli.8 +++ b/sbin/geom/class/eli/geli.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 11, 2006 +.Dd August 9, 2006 .Dt GELI 8 .Os .Sh NAME @@ -52,7 +52,8 @@ utility: .Nm .Cm init .Op Fl bPv -.Op Fl a Ar algo +.Op Fl a Ar aalgo +.Op Fl e Ar ealgo .Op Fl i Ar iterations .Op Fl K Ar newkeyfile .Op Fl l Ar keylen @@ -63,7 +64,7 @@ utility: .Cm init .Nm .Cm attach -.Op Fl dpv +.Op Fl dprv .Op Fl k Ar keyfile .Ar prov .Nm @@ -76,7 +77,8 @@ utility: .Nm .Cm onetime .Op Fl d -.Op Fl a Ar algo +.Op Fl a Ar aalgo +.Op Fl e Ar ealgo .Op Fl l Ar keylen .Op Fl s Ar sectorsize .Ar prov ... @@ -144,6 +146,16 @@ Supports many cryptographic algorithms (currently and .Nm 3DES ) . .It +Can optionally perform data authentication (integrity verification) utilizing +one of the following algorithms: +.Nm HMAC/MD5 , +.Nm HMAC/SHA1 , +.Nm HMAC/RIPEMD160 , +.Nm HMAC/SHA256 , +.Nm HMAC/SHA384 +or +.Nm HMAC/SHA512 . +.It Can create a key from a couple of components (user entered passphrase, random bits from a file, etc.). .It @@ -151,7 +163,7 @@ Allows to encrypt the root partition - the user will be asked for the passphrase before the root file system is mounted. .It The passphrase of the user is strengthened with: -.Rs +.Rs .%A B. Kaliski .%T "PKCS #5: Password-Based Cryptography Specification, Version 2.0." .%R RFC @@ -163,7 +175,7 @@ Allows to use two independent keys (e.g. and .Qq "company key" ) . .It -It is fast - +It is fast - .Nm performs simple sector-to-sector encryption. .It @@ -177,6 +189,8 @@ the file systems). .It Allows to attach a provider with a random, one-time key - useful for swap partitions and temporary file systems. +.It +Allows to verify data integrity (data authentication). .El .Pp The first argument to @@ -189,8 +203,23 @@ Here you can set up the cryptographic algorithm to use, key length, etc. The last provider's sector is used to store metadata. .Pp Additional options include: -.Bl -tag -width ".Fl a Ar algo" -.It Fl a Ar algo +.Bl -tag -width ".Fl a Ar aalgo" +.It Fl a Ar aalgo +Enable data integrity verification (authentication) using the given algorithm. +This will reduce size of available storage and also reduce speed. +For example, when using 4096 bytes sector and +.Nm HMAC/SHA256 +algorithm, 89% of the original provider storage will be available for use. +Currently supported algorithms are: +.Nm HMAC/MD5 , +.Nm HMAC/SHA1 , +.Nm HMAC/RIPEMD160 , +.Nm HMAC/SHA256 , +.Nm HMAC/SHA384 +and +.Nm HMAC/SHA512 . +If the option is not given, there will be no authentication, only encryption. +.It Fl e Ar ealgo Encryption algorithm to use. Currently supported algorithms are: .Nm AES , @@ -259,6 +288,9 @@ Probably a better choice is the option for the .Cm detach subcommand. +.It Fl r +Attach read-only provider. +It will not be opened for writing. .It Fl k Ar keyfile Specifies a file which contains part of the key. For more information see the description of the @@ -289,8 +321,13 @@ Attach the given providers with random, one-time keys. The command can be used to encrypt swap partitions or temporary file systems. .Pp Additional options include: -.Bl -tag -width ".Fl a Ar algo" -.It Fl a Ar algo +.Bl -tag -width ".Fl a Ar aalgo" +.It Fl a Ar aalgo +Enable data integrity verification (authentication). +For more information, see the description of the +.Cm init +subcommand. +.It Fl e Ar ealgo Encryption algorithm to use. For more information, see the description of the .Cm init @@ -415,6 +452,8 @@ variables can be used to control the behavior of the .Nm ELI GEOM class. The default value is shown next to each variable. +All variables can also be set in +.Pa /boot/loader.conf . .Bl -tag -width indent .It Va kern.geom.eli.debug : No 0 Debug level of the @@ -424,8 +463,6 @@ This can be set to a number between 0 and 3 inclusive. If set to 0, minimal debug information is printed. If set to 3, the maximum amount of debug information is printed. -This variable could be set in -.Pa /boot/loader.conf . .It Va kern.geom.eli.tries : No 3 Number of times a user is asked for the passphrase. This is only used for providers which should be attached on boot @@ -451,8 +488,11 @@ cryptography. Its purpose is to increase performance on SMP systems. If hardware acceleration is available, only one thread will be started. If set to 0, CPU-bound thread will be started for every active CPU. -This variable could be set in -.Pa /boot/loader.conf . +.It Va kern.geom.eli.batch : No 0 +When set to 1, can speed-up crypto operations by using batching. +Batching allows to reduce number of interrupts by responding on a group of +crypto requests with one interrupt. +The crypto card and the driver has to support this feature. .El .Sh EXIT STATUS Exit status is 0 on success, and 1 if the command fails. @@ -508,7 +548,7 @@ Reenter new passphrase: Encrypted swap partition setup: .Bd -literal -offset indent # dd if=/dev/random of=/dev/ad0s1b bs=1m -# geli onetime -d -a 3des ad0s1b +# geli onetime -d -e 3des ad0s1b # swapon /dev/ad0s1b.eli .Ed .Pp @@ -546,6 +586,38 @@ geli_da1s3a_keyfile0_load="YES" geli_da1s3a_keyfile0_type="da1s3a:geli_keyfile0" geli_da1s3a_keyfile0_name="/boot/keys/da1s3a.key" .Ed +.Pp +Not only configure encryption, but also data integrity verification using +.Nm HMAC/SHA256 . +.Bd -literal -offset indent +# geli init -a hmac/sha256 -s 4096 /dev/da0 +Enter new passphrase: +Reenter new passphrase: +# geli attach /dev/da0 +Enter passphrase: +# dd if=/dev/random of=/dev/da0.eli bs=1m +# newfs /dev/da0.eli +# mount /dev/da0.eli /mnt/secret +.Ed +.Sh DATA AUTHENTICATION +.Nm +can verify data integrity when an authentication algorithm is specified. +When data corruption/modification is detected, +.Nm +will not return any data, but instead will return an error +.Pq Er EINVAL . +The offset and size of the corrupted data will be printed on the console. +It is important to know against which attacks +.Nm +provides protection for your data. +If data is modified or copied from one place on the disk +to another, +.Nm +should be able to detect such a modification. +If an attacker can remember the encrypted data, modify them and write them +back to the same place, the modification will not be detected. +.Nm +will not protect your data against replay attacks. .Sh SEE ALSO .Xr crypto 4 , .Xr gbde 4 , diff --git a/sbin/geom/class/eli/geom_eli.c b/sbin/geom/class/eli/geom_eli.c index e71adc9e4d02..d9543aea8a5f 100644 --- a/sbin/geom/class/eli/geom_eli.c +++ b/sbin/geom/class/eli/geom_eli.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2004 Pawel Jakub Dawidek + * Copyright (c) 2004-2006 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -54,7 +54,8 @@ __FBSDID("$FreeBSD$"); uint32_t lib_version = G_LIB_VERSION; uint32_t version = G_ELI_VERSION; -static char algo[] = "aes"; +static char aalgo[] = "none"; +static char ealgo[] = "aes"; static intmax_t keylen = 0; static intmax_t keyno = -1; static intmax_t iterations = -1; @@ -75,12 +76,12 @@ static void eli_dump(struct gctl_req *req); /* * Available commands: * - * init [-bhPv] [-a algo] [-i iterations] [-l keylen] [-K newkeyfile] prov + * init [-bhPv] [-a aalgo] [-e ealgo] [-i iterations] [-l keylen] [-K newkeyfile] prov * label - alias for 'init' - * attach [-dpv] [-k keyfile] prov + * attach [-dprv] [-k keyfile] prov * detach [-fl] prov ... * stop - alias for 'detach' - * onetime [-d] [-a algo] [-l keylen] prov ... + * onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov ... * setkey [-pPv] [-n keyno] [-k keyfile] [-K newkeyfile] prov * delkey [-afv] [-n keyno] prov * kill [-av] [prov ...] @@ -92,8 +93,9 @@ static void eli_dump(struct gctl_req *req); struct g_command class_commands[] = { { "init", G_FLAG_VERBOSE, eli_main, { - { 'a', "algo", algo, G_TYPE_STRING }, + { 'a', "aalgo", aalgo, G_TYPE_STRING }, { 'b', "boot", NULL, G_TYPE_NONE }, + { 'e', "ealgo", ealgo, G_TYPE_STRING }, { 'i', "iterations", &iterations, G_TYPE_NUMBER }, { 'K', "newkeyfile", newkeyfile, G_TYPE_STRING }, { 'l', "keylen", &keylen, G_TYPE_NUMBER }, @@ -101,12 +103,13 @@ struct g_command class_commands[] = { { 's', "sectorsize", §orsize, G_TYPE_NUMBER }, G_OPT_SENTINEL }, - "[-bPv] [-a algo] [-i iterations] [-l keylen] [-K newkeyfile] [-s sectorsize] prov" + "[-bPv] [-a aalgo] [-e ealgo] [-i iterations] [-l keylen] [-K newkeyfile] [-s sectorsize] prov" }, { "label", G_FLAG_VERBOSE, eli_main, { - { 'a', "algo", algo, G_TYPE_STRING }, + { 'a', "aalgo", aalgo, G_TYPE_STRING }, { 'b', "boot", NULL, G_TYPE_NONE }, + { 'e', "ealgo", ealgo, G_TYPE_STRING }, { 'i', "iterations", &iterations, G_TYPE_NUMBER }, { 'K', "newkeyfile", newkeyfile, G_TYPE_STRING }, { 'l', "keylen", &keylen, G_TYPE_NUMBER }, @@ -121,9 +124,10 @@ struct g_command class_commands[] = { { 'd', "detach", NULL, G_TYPE_NONE }, { 'k', "keyfile", keyfile, G_TYPE_STRING }, { 'p', "nopassphrase", NULL, G_TYPE_NONE }, + { 'r', "readonly", NULL, G_TYPE_NONE }, G_OPT_SENTINEL }, - "[-dpv] [-k keyfile] prov" + "[-dprv] [-k keyfile] prov" }, { "detach", 0, NULL, { @@ -143,13 +147,14 @@ struct g_command class_commands[] = { }, { "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, { - { 'a', "algo", algo, G_TYPE_STRING }, + { 'a', "aalgo", aalgo, G_TYPE_STRING }, { 'd', "detach", NULL, G_TYPE_NONE }, + { 'e', "ealgo", ealgo, G_TYPE_STRING }, { 'l', "keylen", &keylen, G_TYPE_NUMBER }, { 's', "sectorsize", §orsize, G_TYPE_NUMBER }, G_OPT_SENTINEL }, - "[-d] [-a algo] [-l keylen] [-s sectorsize] prov ..." + "[-d] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov ..." }, { "setkey", G_FLAG_VERBOSE, eli_main, { @@ -389,7 +394,7 @@ eli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key, } } /* - * If md_iterations is equal to 0, user don't want PKCS5v2. + * If md_iterations is equal to 0, user don't want PKCS#5v2. */ if (md->md_iterations == 0) { g_eli_crypto_hmac_update(&ctx, md->md_salt, @@ -523,16 +528,44 @@ eli_init(struct gctl_req *req) md.md_flags = 0; if (gctl_get_int(req, "boot")) md.md_flags |= G_ELI_FLAG_BOOT; - str = gctl_get_ascii(req, "algo"); - md.md_algo = g_eli_str2algo(str); - if (md.md_algo < CRYPTO_ALGORITHM_MIN || - md.md_algo > CRYPTO_ALGORITHM_MAX) { - gctl_error(req, "Invalid encryption algorithm."); - return; + md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1; + str = gctl_get_ascii(req, "aalgo"); + if (strcmp(str, "none") != 0) { + md.md_aalgo = g_eli_str2aalgo(str); + if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN && + md.md_aalgo <= CRYPTO_ALGORITHM_MAX) { + md.md_flags |= G_ELI_FLAG_AUTH; + } else { + /* + * For backward compatibility, check if the -a option + * was used to provide encryption algorithm. + */ + md.md_ealgo = g_eli_str2ealgo(str); + if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || + md.md_ealgo > CRYPTO_ALGORITHM_MAX) { + gctl_error(req, + "Invalid authentication algorithm."); + return; + } else { + fprintf(stderr, "warning: The -e option, not " + "the -a option is now used to specify " + "encryption algorithm to use.\n"); + } + } + } + if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || + md.md_ealgo > CRYPTO_ALGORITHM_MAX) { + str = gctl_get_ascii(req, "ealgo"); + md.md_ealgo = g_eli_str2ealgo(str); + if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || + md.md_ealgo > CRYPTO_ALGORITHM_MAX) { + gctl_error(req, "Invalid encryption algorithm."); + return; + } } val = gctl_get_intmax(req, "keylen"); md.md_keylen = val; - md.md_keylen = g_eli_keylen(md.md_algo, md.md_keylen); + md.md_keylen = g_eli_keylen(md.md_ealgo, md.md_keylen); if (md.md_keylen == 0) { gctl_error(req, "Invalid key length."); return; @@ -579,7 +612,7 @@ eli_init(struct gctl_req *req) } /* Encrypt the first and the only Master Key. */ - error = g_eli_mkey_encrypt(md.md_algo, key, md.md_keylen, md.md_mkeys); + error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, md.md_mkeys); bzero(key, sizeof(key)); if (error != 0) { bzero(&md, sizeof(md)); @@ -733,7 +766,7 @@ eli_setkey_detached(struct gctl_req *req, const char *prov, } /* Encrypt the Master-Key with the new key. */ - error = g_eli_mkey_encrypt(md->md_algo, key, md->md_keylen, mkeydst); + error = g_eli_mkey_encrypt(md->md_ealgo, key, md->md_keylen, mkeydst); bzero(key, sizeof(key)); if (error != 0) { bzero(md, sizeof(*md)); diff --git a/sys/geom/eli/g_eli.c b/sys/geom/eli/g_eli.c index e57e405253c6..51846fc762da 100644 --- a/sys/geom/eli/g_eli.c +++ b/sys/geom/eli/g_eli.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2005 Pawel Jakub Dawidek + * Copyright (c) 2005-2006 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -69,16 +69,20 @@ SYSCTL_UINT(_kern_geom_eli, OID_AUTO, visible_passphrase, CTLFLAG_RW, &g_eli_visible_passphrase, 0, "Turn on echo when entering the passphrase (for debug purposes only!!)"); u_int g_eli_overwrites = 5; +TUNABLE_INT("kern.geom.eli.overwrites", &g_eli_overwrites); SYSCTL_UINT(_kern_geom_eli, OID_AUTO, overwrites, CTLFLAG_RW, &g_eli_overwrites, 0, "Number of times on-disk keys should be overwritten when destroying them"); static u_int g_eli_threads = 0; TUNABLE_INT("kern.geom.eli.threads", &g_eli_threads); SYSCTL_UINT(_kern_geom_eli, OID_AUTO, threads, CTLFLAG_RW, &g_eli_threads, 0, "Number of threads doing crypto work"); +u_int g_eli_batch = 0; +TUNABLE_INT("kern.geom.eli.batch", &g_eli_batch); +SYSCTL_UINT(_kern_geom_eli, OID_AUTO, batch, CTLFLAG_RW, &g_eli_batch, 0, + "Use crypto operations batching"); static int g_eli_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp); -static void g_eli_crypto_run(struct g_eli_worker *wr, struct bio *bp); static g_taste_t g_eli_taste; static g_dumpconf_t g_eli_dumpconf; @@ -106,7 +110,7 @@ struct g_class g_eli_class = { * accelerator or something like this. * The function updates the SID and rerun the operation. */ -static int +int g_eli_crypto_rerun(struct cryptop *crp) { struct g_eli_softc *sc; @@ -139,7 +143,7 @@ g_eli_crypto_rerun(struct cryptop *crp) * * g_eli_start -> g_io_request -> G_ELI_READ_DONE -> g_eli_crypto_run -> g_eli_crypto_read_done -> g_io_deliver */ -static void +void g_eli_read_done(struct bio *bp) { struct g_eli_softc *sc; @@ -149,10 +153,20 @@ g_eli_read_done(struct bio *bp) pbp = bp->bio_parent; if (pbp->bio_error == 0) pbp->bio_error = bp->bio_error; + /* + * Do we have all sectors already? + */ + pbp->bio_inbed++; + if (pbp->bio_inbed < pbp->bio_children) + return; g_destroy_bio(bp); if (pbp->bio_error != 0) { G_ELI_LOGREQ(0, pbp, "%s() failed", __func__); pbp->bio_completed = 0; + if (pbp->bio_driver2 != NULL) { + free(pbp->bio_driver2, M_ELI); + pbp->bio_driver2 = NULL; + } g_io_deliver(pbp, pbp->bio_error); return; } @@ -163,70 +177,31 @@ g_eli_read_done(struct bio *bp) wakeup(sc); } -/* - * The function is called after we read and decrypt data. - * - * g_eli_start -> g_io_request -> g_eli_read_done -> g_eli_crypto_run -> G_ELI_CRYPTO_READ_DONE -> g_io_deliver - */ -static int -g_eli_crypto_read_done(struct cryptop *crp) -{ - struct bio *bp; - - if (crp->crp_etype == EAGAIN) { - if (g_eli_crypto_rerun(crp) == 0) - return (0); - } - bp = (struct bio *)crp->crp_opaque; - bp->bio_inbed++; - if (crp->crp_etype == 0) { - G_ELI_DEBUG(3, "Crypto READ request done (%d/%d).", - bp->bio_inbed, bp->bio_children); - bp->bio_completed += crp->crp_olen; - } else { - G_ELI_DEBUG(1, "Crypto READ request failed (%d/%d) error=%d.", - bp->bio_inbed, bp->bio_children, crp->crp_etype); - if (bp->bio_error == 0) - bp->bio_error = crp->crp_etype; - } - /* - * Do we have all sectors already? - */ - if (bp->bio_inbed < bp->bio_children) - return (0); - free(bp->bio_driver2, M_ELI); - bp->bio_driver2 = NULL; - if (bp->bio_error != 0) { - G_ELI_LOGREQ(0, bp, "Crypto READ request failed (error=%d).", - bp->bio_error); - bp->bio_completed = 0; - } - /* - * Read is finished, send it up. - */ - g_io_deliver(bp, bp->bio_error); - return (0); -} - /* * The function is called after we encrypt and write data. * * g_eli_start -> g_eli_crypto_run -> g_eli_crypto_write_done -> g_io_request -> G_ELI_WRITE_DONE -> g_io_deliver */ -static void +void g_eli_write_done(struct bio *bp) { struct bio *pbp; G_ELI_LOGREQ(2, bp, "Request done."); pbp = bp->bio_parent; - if (pbp->bio_error == 0) - pbp->bio_error = bp->bio_error; + if (pbp->bio_error == 0) { + if (bp->bio_error != 0) + pbp->bio_error = bp->bio_error; + } + /* + * Do we have all sectors already? + */ + pbp->bio_inbed++; + if (pbp->bio_inbed < pbp->bio_children) + return; free(pbp->bio_driver2, M_ELI); pbp->bio_driver2 = NULL; - if (pbp->bio_error == 0) - pbp->bio_completed = pbp->bio_length; - else { + if (pbp->bio_error != 0) { G_ELI_LOGREQ(0, pbp, "Crypto WRITE request failed (error=%d).", pbp->bio_error); pbp->bio_completed = 0; @@ -235,67 +210,10 @@ g_eli_write_done(struct bio *bp) /* * Write is finished, send it up. */ + pbp->bio_completed = pbp->bio_length; g_io_deliver(pbp, pbp->bio_error); } -/* - * The function is called after data encryption. - * - * g_eli_start -> g_eli_crypto_run -> G_ELI_CRYPTO_WRITE_DONE -> g_io_request -> g_eli_write_done -> g_io_deliver - */ -static int -g_eli_crypto_write_done(struct cryptop *crp) -{ - struct g_geom *gp; - struct g_consumer *cp; - struct bio *bp, *cbp; - - if (crp->crp_etype == EAGAIN) { - if (g_eli_crypto_rerun(crp) == 0) - return (0); - } - bp = (struct bio *)crp->crp_opaque; - bp->bio_inbed++; - if (crp->crp_etype == 0) { - G_ELI_DEBUG(3, "Crypto WRITE request done (%d/%d).", - bp->bio_inbed, bp->bio_children); - } else { - G_ELI_DEBUG(1, "Crypto WRITE request failed (%d/%d) error=%d.", - bp->bio_inbed, bp->bio_children, crp->crp_etype); - if (bp->bio_error == 0) - bp->bio_error = crp->crp_etype; - } - /* - * All sectors are already encrypted? - */ - if (bp->bio_inbed < bp->bio_children) - return (0); - bp->bio_inbed = 0; - bp->bio_children = 1; - cbp = bp->bio_driver1; - bp->bio_driver1 = NULL; - if (bp->bio_error != 0) { - G_ELI_LOGREQ(0, bp, "Crypto WRITE request failed (error=%d).", - bp->bio_error); - free(bp->bio_driver2, M_ELI); - bp->bio_driver2 = NULL; - g_destroy_bio(cbp); - g_io_deliver(bp, bp->bio_error); - return (0); - } - cbp->bio_data = bp->bio_driver2; - cbp->bio_done = g_eli_write_done; - gp = bp->bio_to->geom; - cp = LIST_FIRST(&gp->consumer); - cbp->bio_to = cp->provider; - G_ELI_LOGREQ(2, cbp, "Sending request."); - /* - * Send encrypted data to the provider. - */ - g_io_request(cbp, cp); - return (0); -} - /* * This function should never be called, but GEOM made as it set ->orphan() * method for every geom. @@ -358,15 +276,20 @@ g_eli_start(struct bio *bp) } switch (bp->bio_cmd) { case BIO_READ: - cbp->bio_done = g_eli_read_done; - cp = LIST_FIRST(&sc->sc_geom->consumer); - cbp->bio_to = cp->provider; - G_ELI_LOGREQ(2, cbp, "Sending request."); - /* - * Read encrypted data from provider. - */ - g_io_request(cbp, cp); - break; + if (!(sc->sc_flags & G_ELI_FLAG_AUTH)) { + bp->bio_driver2 = NULL; + cbp->bio_done = g_eli_read_done; + cp = LIST_FIRST(&sc->sc_geom->consumer); + cbp->bio_to = cp->provider; + G_ELI_LOGREQ(2, cbp, "Sending request."); + /* + * Read encrypted data from provider. + */ + g_io_request(cbp, cp); + break; + } + bp->bio_pflags = 255; + /* FALLTHROUGH */ case BIO_WRITE: bp->bio_driver1 = cbp; mtx_lock(&sc->sc_queue_mtx); @@ -411,7 +334,7 @@ g_eli_worker(void *arg) mtx_lock(&sc->sc_queue_mtx); bp = bioq_takefirst(&sc->sc_queue); if (bp == NULL) { - if ((sc->sc_flags & G_ELI_FLAG_DESTROY) != 0) { + if (sc->sc_flags & G_ELI_FLAG_DESTROY) { LIST_REMOVE(wr, w_next); crypto_freesession(wr->w_sid); free(wr, M_ELI); @@ -426,20 +349,27 @@ g_eli_worker(void *arg) continue; } mtx_unlock(&sc->sc_queue_mtx); - g_eli_crypto_run(wr, bp); + if (bp->bio_cmd == BIO_READ && bp->bio_pflags == 255) + g_eli_auth_read(sc, bp); + else if (sc->sc_flags & G_ELI_FLAG_AUTH) + g_eli_auth_run(wr, bp); + else + g_eli_crypto_run(wr, bp); } } /* * Here we generate IV. It is unique for every sector. */ -static void +void g_eli_crypto_ivgen(struct g_eli_softc *sc, off_t offset, u_char *iv, size_t size) { - u_char hash[SHA256_DIGEST_LENGTH]; + u_char off[8], hash[SHA256_DIGEST_LENGTH]; SHA256_CTX ctx; + if (!(sc->sc_flags & G_ELI_FLAG_NATIVE_BYTE_ORDER)) + le64enc(off, (uint64_t)offset); /* Copy precalculated SHA256 context for IV-Key. */ bcopy(&sc->sc_ivctx, &ctx, sizeof(ctx)); SHA256_Update(&ctx, (uint8_t *)&offset, sizeof(offset)); @@ -447,110 +377,6 @@ g_eli_crypto_ivgen(struct g_eli_softc *sc, off_t offset, u_char *iv, bcopy(hash, iv, size); } -/* - * This is the main function responsible for cryptography (ie. communication - * with crypto(9) subsystem). - */ -static void -g_eli_crypto_run(struct g_eli_worker *wr, struct bio *bp) -{ - struct g_eli_softc *sc; - struct cryptop *crp; - struct cryptodesc *crd; - struct uio *uio; - struct iovec *iov; - u_int i, nsec, add, secsize; - int err, error; - size_t size; - u_char *p, *data; - - G_ELI_LOGREQ(3, bp, "%s", __func__); - - bp->bio_pflags = wr->w_number; - sc = wr->w_softc; - secsize = LIST_FIRST(&sc->sc_geom->provider)->sectorsize; - nsec = bp->bio_length / secsize; - - /* - * Calculate how much memory do we need. - * We need separate crypto operation for every single sector. - * It is much faster to calculate total amount of needed memory here and - * do the allocation once instead of allocating memory in pieces (many, - * many pieces). - */ - size = sizeof(*crp) * nsec; - size += sizeof(*crd) * nsec; - size += sizeof(*uio) * nsec; - size += sizeof(*iov) * nsec; - /* - * If we write the data we cannot destroy current bio_data content, - * so we need to allocate more memory for encrypted data. - */ - if (bp->bio_cmd == BIO_WRITE) - size += bp->bio_length; - p = malloc(size, M_ELI, M_WAITOK); - - bp->bio_inbed = 0; - bp->bio_children = nsec; - bp->bio_driver2 = p; - - if (bp->bio_cmd == BIO_READ) - data = bp->bio_data; - else { - data = p; - p += bp->bio_length; - bcopy(bp->bio_data, data, bp->bio_length); - } - - error = 0; - for (i = 0, add = 0; i < nsec; i++, add += secsize) { - crp = (struct cryptop *)p; p += sizeof(*crp); - crd = (struct cryptodesc *)p; p += sizeof(*crd); - uio = (struct uio *)p; p += sizeof(*uio); - iov = (struct iovec *)p; p += sizeof(*iov); - - iov->iov_len = secsize; - iov->iov_base = data; - data += secsize; - - uio->uio_iov = iov; - uio->uio_iovcnt = 1; - uio->uio_segflg = UIO_SYSSPACE; - uio->uio_resid = secsize; - - crp->crp_sid = wr->w_sid; - crp->crp_ilen = secsize; - crp->crp_olen = secsize; - crp->crp_opaque = (void *)bp; - crp->crp_buf = (void *)uio; - if (bp->bio_cmd == BIO_WRITE) - crp->crp_callback = g_eli_crypto_write_done; - else /* if (bp->bio_cmd == BIO_READ) */ - crp->crp_callback = g_eli_crypto_read_done; - crp->crp_flags = CRYPTO_F_IOV | CRYPTO_F_CBIFSYNC | CRYPTO_F_REL; - crp->crp_desc = crd; - - crd->crd_skip = 0; - crd->crd_len = secsize; - crd->crd_flags = CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT; - if (bp->bio_cmd == BIO_WRITE) - crd->crd_flags |= CRD_F_ENCRYPT; - crd->crd_alg = sc->sc_algo; - crd->crd_key = sc->sc_datakey; - crd->crd_klen = sc->sc_keylen; - g_eli_crypto_ivgen(sc, bp->bio_offset + add, crd->crd_iv, - sizeof(crd->crd_iv)); - crd->crd_next = NULL; - - crp->crp_etype = 0; - err = crypto_dispatch(crp); - if (error == 0) - error = err; - } - if (bp->bio_error == 0) - bp->bio_error = error; -} - int g_eli_read_metadata(struct g_class *mp, struct g_provider *pp, struct g_eli_metadata *md) @@ -631,6 +457,10 @@ g_eli_access(struct g_provider *pp, int dr, int dw, int de) sc = gp->softc; if (dw > 0) { + if (sc->sc_flags & G_ELI_FLAG_RO) { + /* Deny write attempts. */ + return (EROFS); + } /* Someone is opening us for write, we need to remember that. */ sc->sc_flags |= G_ELI_FLAG_WOPEN; return (0); @@ -658,7 +488,7 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, struct g_geom *gp; struct g_provider *pp; struct g_consumer *cp; - struct cryptoini cri; + struct cryptoini crie, cria; u_int i, threads; int error; @@ -671,31 +501,60 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, gp->start = g_eli_start; /* * Spoiling cannot happen actually, because we keep provider open for - * writing all the time. + * writing all the time or provider is read-only. */ gp->spoiled = g_eli_orphan_spoil_assert; gp->orphan = g_eli_orphan; + gp->dumpconf = g_eli_dumpconf; /* - * If detach-on-last-close feature is not enabled, we can simply use - * g_std_access(). + * If detach-on-last-close feature is not enabled and we don't operate + * on read-only provider, we can simply use g_std_access(). */ - if (md->md_flags & G_ELI_FLAG_WO_DETACH) + if (md->md_flags & (G_ELI_FLAG_WO_DETACH | G_ELI_FLAG_RO)) gp->access = g_eli_access; else gp->access = g_std_access; - gp->dumpconf = g_eli_dumpconf; sc->sc_crypto = G_ELI_CRYPTO_SW; sc->sc_flags = md->md_flags; - sc->sc_algo = md->md_algo; + /* Backward compatibility. */ + if (md->md_version < 2) + sc->sc_flags |= G_ELI_FLAG_NATIVE_BYTE_ORDER; + sc->sc_ealgo = md->md_ealgo; sc->sc_nkey = nkey; /* * Remember the keys in our softc structure. */ - bcopy(mkey, sc->sc_ivkey, sizeof(sc->sc_ivkey)); - mkey += sizeof(sc->sc_ivkey); - bcopy(mkey, sc->sc_datakey, sizeof(sc->sc_datakey)); - sc->sc_keylen = md->md_keylen; + g_eli_mkey_propagate(sc, mkey); + sc->sc_ekeylen = md->md_keylen; + + if (sc->sc_flags & G_ELI_FLAG_AUTH) { + sc->sc_akeylen = sizeof(sc->sc_akey) * 8; + sc->sc_aalgo = md->md_aalgo; + sc->sc_alen = g_eli_hashlen(sc->sc_aalgo); + + sc->sc_data_per_sector = bpp->sectorsize - sc->sc_alen; + /* + * Some hash functions (like SHA1 and RIPEMD160) generates hash + * which length is not multiple of 128 bits, but we want data + * length to be multiple of 128, so we can encrypt without + * padding. The line below rounds down data length to multiple + * of 128 bits. + */ + sc->sc_data_per_sector -= sc->sc_data_per_sector % 16; + + sc->sc_bytes_per_sector = + (md->md_sectorsize - 1) / sc->sc_data_per_sector + 1; + sc->sc_bytes_per_sector *= bpp->sectorsize; + /* + * Precalculate SHA256 for HMAC key generation. + * This is expensive operation and we can do it only once now or + * for every access to sector, so now will be much better. + */ + SHA256_Init(&sc->sc_akeyctx); + SHA256_Update(&sc->sc_akeyctx, sc->sc_akey, + sizeof(sc->sc_akey)); + } /* * Precalculate SHA256 for IV generation. @@ -728,8 +587,13 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, * Keep provider open all the time, so we can run critical tasks, * like Master Keys deletion, without wondering if we can open * provider or not. + * We don't open provider for writing only when user requested read-only + * access. */ - error = g_access(cp, 1, 1, 1); + if (sc->sc_flags & G_ELI_FLAG_RO) + error = g_access(cp, 1, 0, 1); + else + error = g_access(cp, 1, 1, 1); if (error != 0) { if (req != NULL) { gctl_error(req, "Cannot access %s (error=%d).", @@ -743,10 +607,17 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, LIST_INIT(&sc->sc_workers); - bzero(&cri, sizeof(cri)); - cri.cri_alg = sc->sc_algo; - cri.cri_klen = sc->sc_keylen; - cri.cri_key = sc->sc_datakey; + bzero(&crie, sizeof(crie)); + crie.cri_alg = sc->sc_ealgo; + crie.cri_klen = sc->sc_ekeylen; + crie.cri_key = sc->sc_ekey; + if (sc->sc_flags & G_ELI_FLAG_AUTH) { + bzero(&cria, sizeof(cria)); + cria.cri_alg = sc->sc_aalgo; + cria.cri_klen = sc->sc_akeylen; + cria.cri_key = sc->sc_akey; + crie.cri_next = &cria; + } threads = g_eli_threads; if (threads == 0) @@ -766,12 +637,12 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, * Use software cryptography, if we cannot get it. */ if (i == 0) { - error = crypto_newsession(&wr->w_sid, &cri, 1); + error = crypto_newsession(&wr->w_sid, &crie, 1); if (error == 0) sc->sc_crypto = G_ELI_CRYPTO_HW; } if (sc->sc_crypto == G_ELI_CRYPTO_SW) - error = crypto_newsession(&wr->w_sid, &cri, 0); + error = crypto_newsession(&wr->w_sid, &crie, 0); if (error != 0) { free(wr, M_ELI); if (req != NULL) { @@ -810,14 +681,22 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, pp = g_new_providerf(gp, "%s%s", bpp->name, G_ELI_SUFFIX); pp->sectorsize = md->md_sectorsize; pp->mediasize = bpp->mediasize; - if ((sc->sc_flags & G_ELI_FLAG_ONETIME) == 0) + if (!(sc->sc_flags & G_ELI_FLAG_ONETIME)) pp->mediasize -= bpp->sectorsize; - pp->mediasize -= (pp->mediasize % pp->sectorsize); + if (!(sc->sc_flags & G_ELI_FLAG_AUTH)) + pp->mediasize -= (pp->mediasize % pp->sectorsize); + else { + pp->mediasize /= sc->sc_bytes_per_sector; + pp->mediasize *= pp->sectorsize; + } + g_error_provider(pp, 0); G_ELI_DEBUG(0, "Device %s created.", pp->name); - G_ELI_DEBUG(0, " Cipher: %s", g_eli_algo2str(sc->sc_algo)); - G_ELI_DEBUG(0, "Key length: %u", sc->sc_keylen); + G_ELI_DEBUG(0, "Encryption: %s %u", g_eli_algo2str(sc->sc_ealgo), + sc->sc_ekeylen); + if (sc->sc_flags & G_ELI_FLAG_AUTH) + G_ELI_DEBUG(0, " Integrity: %s", g_eli_algo2str(sc->sc_aalgo)); G_ELI_DEBUG(0, " Crypto: %s", sc->sc_crypto == G_ELI_CRYPTO_SW ? "software" : "hardware"); return (gp); @@ -995,7 +874,7 @@ g_eli_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) if (md.md_provsize != pp->mediasize) return (NULL); /* Should we attach it on boot? */ - if ((md.md_flags & G_ELI_FLAG_BOOT) == 0) + if (!(md.md_flags & G_ELI_FLAG_BOOT)) return (NULL); if (md.md_keys == 0x00) { G_ELI_DEBUG(0, "No valid keys on %s.", pp->name); @@ -1117,7 +996,7 @@ g_eli_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, int first = 1; #define ADD_FLAG(flag, name) do { \ - if ((sc->sc_flags & (flag)) != 0) { \ + if (sc->sc_flags & (flag)) { \ if (!first) \ sbuf_printf(sb, ", "); \ else \ @@ -1125,17 +1004,20 @@ g_eli_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, sbuf_printf(sb, name); \ } \ } while (0) + ADD_FLAG(G_ELI_FLAG_NATIVE_BYTE_ORDER, "NATIVE-BYTE-ORDER"); ADD_FLAG(G_ELI_FLAG_ONETIME, "ONETIME"); ADD_FLAG(G_ELI_FLAG_BOOT, "BOOT"); ADD_FLAG(G_ELI_FLAG_WO_DETACH, "W-DETACH"); ADD_FLAG(G_ELI_FLAG_RW_DETACH, "RW-DETACH"); + ADD_FLAG(G_ELI_FLAG_AUTH, "AUTH"); ADD_FLAG(G_ELI_FLAG_WOPEN, "W-OPEN"); ADD_FLAG(G_ELI_FLAG_DESTROY, "DESTROY"); + ADD_FLAG(G_ELI_FLAG_RO, "READ-ONLY"); #undef ADD_FLAG } sbuf_printf(sb, "\n"); - if ((sc->sc_flags & G_ELI_FLAG_ONETIME) == 0) { + if (!(sc->sc_flags & G_ELI_FLAG_ONETIME)) { sbuf_printf(sb, "%s%u\n", indent, sc->sc_nkey); } @@ -1152,9 +1034,15 @@ g_eli_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, break; } sbuf_printf(sb, "\n"); - sbuf_printf(sb, "%s%u\n", indent, sc->sc_keylen); - sbuf_printf(sb, "%s%s\n", indent, - g_eli_algo2str(sc->sc_algo)); + if (sc->sc_flags & G_ELI_FLAG_AUTH) { + sbuf_printf(sb, + "%s%s\n", + indent, g_eli_algo2str(sc->sc_aalgo)); + } + sbuf_printf(sb, "%s%u\n", indent, + sc->sc_ekeylen); + sbuf_printf(sb, "%s%s\n", indent, + g_eli_algo2str(sc->sc_ealgo)); } DECLARE_GEOM_CLASS(g_eli_class, g_eli); diff --git a/sys/geom/eli/g_eli.h b/sys/geom/eli/g_eli.h index 363b38b4502c..e24f31491119 100644 --- a/sys/geom/eli/g_eli.h +++ b/sys/geom/eli/g_eli.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2005 Pawel Jakub Dawidek + * Copyright (c) 2005-2006 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -54,28 +54,43 @@ /* * Version history: * 0 - Initial version number. + * 1 - Added data authentication support (md_aalgo field and + * G_ELI_FLAG_AUTH flag). + * 2 - Added G_ELI_FLAG_READONLY. + * - IV is generated from offset converted to little-endian + * (flag G_ELI_FLAG_NATIVE_BYTE_ORDER will be set for older versions). */ -#define G_ELI_VERSION 0 +#define G_ELI_VERSION 2 +/* ON DISK FLAGS. */ /* Use random, onetime keys. */ -#define G_ELI_FLAG_ONETIME 0x00000001 +#define G_ELI_FLAG_ONETIME 0x00000001 /* Ask for the passphrase from the kernel, before mounting root. */ -#define G_ELI_FLAG_BOOT 0x00000002 +#define G_ELI_FLAG_BOOT 0x00000002 /* Detach on last close, if we were open for writing. */ -#define G_ELI_FLAG_WO_DETACH 0x00000004 +#define G_ELI_FLAG_WO_DETACH 0x00000004 /* Detach on last close. */ -#define G_ELI_FLAG_RW_DETACH 0x00000008 +#define G_ELI_FLAG_RW_DETACH 0x00000008 +/* Provide data authentication. */ +#define G_ELI_FLAG_AUTH 0x00000010 +/* Provider is read-only, we should deny all write attempts. */ +#define G_ELI_FLAG_RO 0x00000020 +/* RUNTIME FLAGS. */ /* Provider was open for writing. */ -#define G_ELI_FLAG_WOPEN 0x00010000 +#define G_ELI_FLAG_WOPEN 0x00010000 /* Destroy device. */ -#define G_ELI_FLAG_DESTROY 0x00020000 +#define G_ELI_FLAG_DESTROY 0x00020000 +/* Provider uses native byte-order for IV generation. */ +#define G_ELI_FLAG_NATIVE_BYTE_ORDER 0x00040000 #define SHA512_MDLEN 64 +#define G_ELI_AUTH_SECKEYLEN SHA256_DIGEST_LENGTH #define G_ELI_MAXMKEYS 2 #define G_ELI_MAXKEYLEN 64 #define G_ELI_USERKEYLEN G_ELI_MAXKEYLEN #define G_ELI_DATAKEYLEN G_ELI_MAXKEYLEN +#define G_ELI_AUTHKEYLEN G_ELI_MAXKEYLEN #define G_ELI_IVKEYLEN G_ELI_MAXKEYLEN #define G_ELI_SALTLEN 64 #define G_ELI_DATAIVKEYLEN (G_ELI_DATAKEYLEN + G_ELI_IVKEYLEN) @@ -85,6 +100,7 @@ #ifdef _KERNEL extern u_int g_eli_debug; extern u_int g_eli_overwrites; +extern u_int g_eli_batch; #define G_ELI_CRYPTO_HW 1 #define G_ELI_CRYPTO_SW 2 @@ -123,13 +139,21 @@ struct g_eli_worker { struct g_eli_softc { struct g_geom *sc_geom; u_int sc_crypto; - uint8_t sc_datakey[G_ELI_DATAKEYLEN]; + uint8_t sc_mkey[G_ELI_DATAIVKEYLEN]; + uint8_t sc_ekey[G_ELI_DATAKEYLEN]; + u_int sc_ealgo; + u_int sc_ekeylen; + uint8_t sc_akey[G_ELI_AUTHKEYLEN]; + u_int sc_aalgo; + u_int sc_akeylen; + u_int sc_alen; + SHA256_CTX sc_akeyctx; uint8_t sc_ivkey[G_ELI_IVKEYLEN]; SHA256_CTX sc_ivctx; - u_int sc_algo; - u_int sc_keylen; int sc_nkey; uint32_t sc_flags; + u_int sc_bytes_per_sector; + u_int sc_data_per_sector; /* Only for software cryptography. */ struct bio_queue_head sc_queue; @@ -143,8 +167,9 @@ struct g_eli_metadata { char md_magic[16]; /* Magic value. */ uint32_t md_version; /* Version number. */ uint32_t md_flags; /* Additional flags. */ - uint16_t md_algo; /* Encryption algorithm. */ + uint16_t md_ealgo; /* Encryption algorithm. */ uint16_t md_keylen; /* Key length. */ + uint16_t md_aalgo; /* Authentication algorithm. */ uint64_t md_provsize; /* Provider's size. */ uint32_t md_sectorsize; /* Sector size. */ uint8_t md_keys; /* Available keys. */ @@ -165,8 +190,9 @@ eli_metadata_encode(struct g_eli_metadata *md, u_char *data) bcopy(md->md_magic, p, sizeof(md->md_magic)); p += sizeof(md->md_magic); le32enc(p, md->md_version); p += sizeof(md->md_version); le32enc(p, md->md_flags); p += sizeof(md->md_flags); - le16enc(p, md->md_algo); p += sizeof(md->md_algo); + le16enc(p, md->md_ealgo); p += sizeof(md->md_ealgo); le16enc(p, md->md_keylen); p += sizeof(md->md_keylen); + le16enc(p, md->md_aalgo); p += sizeof(md->md_aalgo); le64enc(p, md->md_provsize); p += sizeof(md->md_provsize); le32enc(p, md->md_sectorsize); p += sizeof(md->md_sectorsize); *p = md->md_keys; p += sizeof(md->md_keys); @@ -186,7 +212,7 @@ eli_metadata_decode_v0(const u_char *data, struct g_eli_metadata *md) p = data + sizeof(md->md_magic) + sizeof(md->md_version); md->md_flags = le32dec(p); p += sizeof(md->md_flags); - md->md_algo = le16dec(p); p += sizeof(md->md_algo); + md->md_ealgo = le16dec(p); p += sizeof(md->md_ealgo); md->md_keylen = le16dec(p); p += sizeof(md->md_keylen); md->md_provsize = le64dec(p); p += sizeof(md->md_provsize); md->md_sectorsize = le32dec(p); p += sizeof(md->md_sectorsize); @@ -202,6 +228,30 @@ eli_metadata_decode_v0(const u_char *data, struct g_eli_metadata *md) return (0); } static __inline int +eli_metadata_decode_v1v2(const u_char *data, struct g_eli_metadata *md) +{ + MD5_CTX ctx; + const u_char *p; + + p = data + sizeof(md->md_magic) + sizeof(md->md_version); + md->md_flags = le32dec(p); p += sizeof(md->md_flags); + md->md_ealgo = le16dec(p); p += sizeof(md->md_ealgo); + md->md_keylen = le16dec(p); p += sizeof(md->md_keylen); + md->md_aalgo = le16dec(p); p += sizeof(md->md_aalgo); + md->md_provsize = le64dec(p); p += sizeof(md->md_provsize); + md->md_sectorsize = le32dec(p); p += sizeof(md->md_sectorsize); + md->md_keys = *p; p += sizeof(md->md_keys); + md->md_iterations = le32dec(p); p += sizeof(md->md_iterations); + bcopy(p, md->md_salt, sizeof(md->md_salt)); p += sizeof(md->md_salt); + bcopy(p, md->md_mkeys, sizeof(md->md_mkeys)); p += sizeof(md->md_mkeys); + MD5Init(&ctx); + MD5Update(&ctx, data, p - data); + MD5Final(md->md_hash, &ctx); + if (bcmp(md->md_hash, p, 16) != 0) + return (EINVAL); + return (0); +} +static __inline int eli_metadata_decode(const u_char *data, struct g_eli_metadata *md) { int error; @@ -212,6 +262,10 @@ eli_metadata_decode(const u_char *data, struct g_eli_metadata *md) case 0: error = eli_metadata_decode_v0(data, md); break; + case 1: + case 2: + error = eli_metadata_decode_v1v2(data, md); + break; default: error = EINVAL; break; @@ -221,7 +275,7 @@ eli_metadata_decode(const u_char *data, struct g_eli_metadata *md) #endif /* !_OpenSSL */ static __inline u_int -g_eli_str2algo(const char *name) +g_eli_str2ealgo(const char *name) { if (strcasecmp("null", name) == 0) @@ -235,6 +289,25 @@ g_eli_str2algo(const char *name) return (CRYPTO_ALGORITHM_MIN - 1); } +static __inline u_int +g_eli_str2aalgo(const char *name) +{ + + if (strcasecmp("hmac/md5", name) == 0) + return (CRYPTO_MD5_HMAC); + else if (strcasecmp("hmac/sha1", name) == 0) + return (CRYPTO_SHA1_HMAC); + else if (strcasecmp("hmac/ripemd160", name) == 0) + return (CRYPTO_RIPEMD160_HMAC); + else if (strcasecmp("hmac/sha256", name) == 0) + return (CRYPTO_SHA2_256_HMAC); + else if (strcasecmp("hmac/sha384", name) == 0) + return (CRYPTO_SHA2_384_HMAC); + else if (strcasecmp("hmac/sha512", name) == 0) + return (CRYPTO_SHA2_512_HMAC); + return (CRYPTO_ALGORITHM_MIN - 1); +} + static __inline const char * g_eli_algo2str(u_int algo) { @@ -243,11 +316,23 @@ g_eli_algo2str(u_int algo) case CRYPTO_NULL_CBC: return ("NULL"); case CRYPTO_AES_CBC: - return ("AES"); + return ("AES-CBC"); case CRYPTO_BLF_CBC: - return ("Blowfish"); + return ("Blowfish-CBC"); case CRYPTO_3DES_CBC: - return ("3DES"); + return ("3DES-CBC"); + case CRYPTO_MD5_HMAC: + return ("HMAC/MD5"); + case CRYPTO_SHA1_HMAC: + return ("HMAC/SHA1"); + case CRYPTO_RIPEMD160_HMAC: + return ("HMAC/RIPEMD160"); + case CRYPTO_SHA2_256_HMAC: + return ("HMAC/SHA256"); + case CRYPTO_SHA2_384_HMAC: + return ("HMAC/SHA384"); + case CRYPTO_SHA2_512_HMAC: + return ("HMAC/SHA512"); } return ("unknown"); } @@ -262,8 +347,10 @@ eli_metadata_dump(const struct g_eli_metadata *md) printf(" magic: %s\n", md->md_magic); printf(" version: %u\n", (u_int)md->md_version); printf(" flags: 0x%x\n", (u_int)md->md_flags); - printf(" algo: %s\n", g_eli_algo2str(md->md_algo)); + printf(" ealgo: %s\n", g_eli_algo2str(md->md_ealgo)); printf(" keylen: %u\n", (u_int)md->md_keylen); + if (md->md_flags & G_ELI_FLAG_AUTH) + printf(" aalgo: %s\n", g_eli_algo2str(md->md_aalgo)); printf(" provsize: %ju\n", (uintmax_t)md->md_provsize); printf("sectorsize: %u\n", (u_int)md->md_sectorsize); printf(" keys: 0x%02x\n", (u_int)md->md_keys); @@ -329,6 +416,27 @@ g_eli_keylen(u_int algo, u_int keylen) } } +static __inline u_int +g_eli_hashlen(u_int algo) +{ + + switch (algo) { + case CRYPTO_MD5_HMAC: + return (16); + case CRYPTO_SHA1_HMAC: + return (20); + case CRYPTO_RIPEMD160_HMAC: + return (20); + case CRYPTO_SHA2_256_HMAC: + return (32); + case CRYPTO_SHA2_384_HMAC: + return (48); + case CRYPTO_SHA2_512_HMAC: + return (64); + } + return (0); +} + #ifdef _KERNEL int g_eli_read_metadata(struct g_class *mp, struct g_provider *pp, struct g_eli_metadata *md); @@ -339,6 +447,17 @@ int g_eli_destroy(struct g_eli_softc *sc, boolean_t force); int g_eli_access(struct g_provider *pp, int dr, int dw, int de); void g_eli_config(struct gctl_req *req, struct g_class *mp, const char *verb); + +void g_eli_read_done(struct bio *bp); +void g_eli_write_done(struct bio *bp); +int g_eli_crypto_rerun(struct cryptop *crp); +void g_eli_crypto_ivgen(struct g_eli_softc *sc, off_t offset, u_char *iv, + size_t size); + +void g_eli_crypto_run(struct g_eli_worker *wr, struct bio *bp); + +void g_eli_auth_read(struct g_eli_softc *sc, struct bio *bp); +void g_eli_auth_run(struct g_eli_worker *wr, struct bio *bp); #endif void g_eli_mkey_hmac(unsigned char *mkey, const unsigned char *key); @@ -346,6 +465,9 @@ int g_eli_mkey_decrypt(const struct g_eli_metadata *md, const unsigned char *key, unsigned char *mkey, unsigned *nkeyp); int g_eli_mkey_encrypt(unsigned algo, const unsigned char *key, unsigned keylen, unsigned char *mkey); +#ifdef _KERNEL +void g_eli_mkey_propagate(struct g_eli_softc *sc, const unsigned char *mkey); +#endif int g_eli_crypto_encrypt(u_int algo, u_char *data, size_t datasize, const u_char *key, size_t keysize); diff --git a/sys/geom/eli/g_eli_ctl.c b/sys/geom/eli/g_eli_ctl.c index e8297d318a08..702f8542429e 100644 --- a/sys/geom/eli/g_eli_ctl.c +++ b/sys/geom/eli/g_eli_ctl.c @@ -57,7 +57,7 @@ g_eli_ctl_attach(struct gctl_req *req, struct g_class *mp) struct g_provider *pp; const char *name; u_char *key, mkey[G_ELI_DATAIVKEYLEN]; - int *nargs, *detach; + int *nargs, *detach, *readonly; int keysize, error; u_int nkey; @@ -79,6 +79,12 @@ g_eli_ctl_attach(struct gctl_req *req, struct g_class *mp) return; } + readonly = gctl_get_paraml(req, "readonly", sizeof(*readonly)); + if (readonly == NULL) { + gctl_error(req, "No '%s' argument.", "readonly"); + return; + } + name = gctl_get_asciiparam(req, "arg0"); if (name == NULL) { gctl_error(req, "No 'arg%u' argument.", 0); @@ -124,8 +130,16 @@ g_eli_ctl_attach(struct gctl_req *req, struct g_class *mp) } G_ELI_DEBUG(1, "Using Master Key %u for %s.", nkey, pp->name); + if (*detach && *readonly) { + bzero(&md, sizeof(md)); + gctl_error(req, "Options -d and -r are mutually exclusive.", + pp->name, error); + return; + } if (*detach) md.md_flags |= G_ELI_FLAG_WO_DETACH; + if (*readonly) + md.md_flags |= G_ELI_FLAG_RO; g_eli_create(req, mp, pp, &md, mkey, nkey); bzero(mkey, sizeof(mkey)); bzero(&md, sizeof(md)); @@ -250,16 +264,49 @@ g_eli_ctl_onetime(struct gctl_req *req, struct g_class *mp) if (*detach) md.md_flags |= G_ELI_FLAG_WO_DETACH; - name = gctl_get_asciiparam(req, "algo"); + md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1; + name = gctl_get_asciiparam(req, "aalgo"); if (name == NULL) { - gctl_error(req, "No '%s' argument.", "algo"); + gctl_error(req, "No '%s' argument.", "aalgo"); return; } - md.md_algo = g_eli_str2algo(name); - if (md.md_algo < CRYPTO_ALGORITHM_MIN || - md.md_algo > CRYPTO_ALGORITHM_MAX) { - gctl_error(req, "Invalid '%s' argument.", "algo"); - return; + if (strcmp(name, "none") != 0) { + md.md_aalgo = g_eli_str2aalgo(name); + if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN && + md.md_aalgo <= CRYPTO_ALGORITHM_MAX) { + md.md_flags |= G_ELI_FLAG_AUTH; + } else { + /* + * For backward compatibility, check if the -a option + * was used to provide encryption algorithm. + */ + md.md_ealgo = g_eli_str2ealgo(name); + if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || + md.md_ealgo > CRYPTO_ALGORITHM_MAX) { + gctl_error(req, + "Invalid authentication algorithm."); + return; + } else { + gctl_error(req, "warning: The -e option, not " + "the -a option is now used to specify " + "encryption algorithm to use."); + } + } + } + + if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || + md.md_ealgo > CRYPTO_ALGORITHM_MAX) { + name = gctl_get_asciiparam(req, "ealgo"); + if (name == NULL) { + gctl_error(req, "No '%s' argument.", "ealgo"); + return; + } + md.md_ealgo = g_eli_str2ealgo(name); + if (md.md_ealgo < CRYPTO_ALGORITHM_MIN || + md.md_ealgo > CRYPTO_ALGORITHM_MAX) { + gctl_error(req, "Invalid encryption algorithm."); + return; + } } keylen = gctl_get_paraml(req, "keylen", sizeof(*keylen)); @@ -267,7 +314,7 @@ g_eli_ctl_onetime(struct gctl_req *req, struct g_class *mp) gctl_error(req, "No '%s' argument.", "keylen"); return; } - md.md_keylen = g_eli_keylen(md.md_algo, *keylen); + md.md_keylen = g_eli_keylen(md.md_ealgo, *keylen); if (md.md_keylen == 0) { gctl_error(req, "Invalid '%s' argument.", "keylen"); return; @@ -341,6 +388,10 @@ g_eli_ctl_setkey(struct gctl_req *req, struct g_class *mp) gctl_error(req, "Provider %s is invalid.", name); return; } + if (sc->sc_flags & G_ELI_FLAG_RO) { + gctl_error(req, "Cannot change keys for read-only provider."); + return; + } cp = LIST_FIRST(&sc->sc_geom->consumer); pp = cp->provider; @@ -395,12 +446,10 @@ g_eli_ctl_setkey(struct gctl_req *req, struct g_class *mp) mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN; md.md_keys |= (1 << nkey); - bcopy(sc->sc_ivkey, mkeydst, sizeof(sc->sc_ivkey)); - bcopy(sc->sc_datakey, mkeydst + sizeof(sc->sc_ivkey), - sizeof(sc->sc_datakey)); + bcopy(sc->sc_mkey, mkeydst, sizeof(sc->sc_mkey)); /* Encrypt Master Key with the new key. */ - error = g_eli_mkey_encrypt(md.md_algo, key, md.md_keylen, mkeydst); + error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, mkeydst); bzero(key, sizeof(key)); if (error != 0) { bzero(&md, sizeof(md)); @@ -452,6 +501,10 @@ g_eli_ctl_delkey(struct gctl_req *req, struct g_class *mp) gctl_error(req, "Provider %s is invalid.", name); return; } + if (sc->sc_flags & G_ELI_FLAG_RO) { + gctl_error(req, "Cannot delete keys for read-only provider."); + return; + } cp = LIST_FIRST(&sc->sc_geom->consumer); pp = cp->provider; @@ -534,9 +587,7 @@ g_eli_kill_one(struct g_eli_softc *sc) { struct g_provider *pp; struct g_consumer *cp; - u_char *sector; - int err, error = 0; - u_int i; + int error = 0; g_topology_assert(); @@ -549,22 +600,31 @@ g_eli_kill_one(struct g_eli_softc *sc) cp = LIST_FIRST(&sc->sc_geom->consumer); pp = cp->provider; - sector = malloc(pp->sectorsize, M_ELI, M_WAITOK); - for (i = 0; i <= g_eli_overwrites; i++) { - if (i == g_eli_overwrites) - bzero(sector, pp->sectorsize); - else - arc4rand(sector, pp->sectorsize, 0); - err = g_write_data(cp, pp->mediasize - pp->sectorsize, sector, - pp->sectorsize); - if (err != 0) { - G_ELI_DEBUG(0, "Cannot erase metadata on %s " - "(error=%d).", pp->name, err); - if (error == 0) - error = err; + if (sc->sc_flags & G_ELI_FLAG_RO) { + G_ELI_DEBUG(0, "WARNING: Metadata won't be erased on read-only " + "provider: %s.", pp->name); + } else { + u_char *sector; + u_int i; + int err; + + sector = malloc(pp->sectorsize, M_ELI, M_WAITOK); + for (i = 0; i <= g_eli_overwrites; i++) { + if (i == g_eli_overwrites) + bzero(sector, pp->sectorsize); + else + arc4rand(sector, pp->sectorsize, 0); + err = g_write_data(cp, pp->mediasize - pp->sectorsize, + sector, pp->sectorsize); + if (err != 0) { + G_ELI_DEBUG(0, "Cannot erase metadata on %s " + "(error=%d).", pp->name, err); + if (error == 0) + error = err; + } } + free(sector, M_ELI); } - free(sector, M_ELI); if (error == 0) G_ELI_DEBUG(0, "%s has been killed.", pp->name); g_eli_destroy(sc, 1); diff --git a/sys/geom/eli/g_eli_key.c b/sys/geom/eli/g_eli_key.c index aef66976578a..3f7be95752c2 100644 --- a/sys/geom/eli/g_eli_key.c +++ b/sys/geom/eli/g_eli_key.c @@ -123,10 +123,10 @@ g_eli_mkey_decrypt(const struct g_eli_metadata *md, const unsigned char *key, nkey = 0; for (nkey = 0; nkey < G_ELI_MAXMKEYS; nkey++, mmkey += G_ELI_MKEYLEN) { bit = (1 << nkey); - if ((md->md_keys & bit) == 0) + if (!(md->md_keys & bit)) continue; bcopy(mmkey, tmpmkey, G_ELI_MKEYLEN); - error = g_eli_crypto_decrypt(md->md_algo, tmpmkey, + error = g_eli_crypto_decrypt(md->md_ealgo, tmpmkey, G_ELI_MKEYLEN, enckey, md->md_keylen); if (error != 0) { bzero(tmpmkey, sizeof(tmpmkey)); @@ -177,3 +177,33 @@ g_eli_mkey_encrypt(unsigned algo, const unsigned char *key, unsigned keylen, return (error); } + +#ifdef _KERNEL +/* + * When doing encryption only, copy IV key and encryption key. + * When doing encryption and authentication, copy IV key, generate encryption + * key and generate authentication key. + */ +void +g_eli_mkey_propagate(struct g_eli_softc *sc, const unsigned char *mkey) +{ + + /* Remember the Master Key. */ + bcopy(mkey, sc->sc_mkey, sizeof(sc->sc_mkey)); + + bcopy(mkey, sc->sc_ivkey, sizeof(sc->sc_ivkey)); + mkey += sizeof(sc->sc_ivkey); + + if (!(sc->sc_flags & G_ELI_FLAG_AUTH)) { + bcopy(mkey, sc->sc_ekey, sizeof(sc->sc_ekey)); + } else { + /* + * The encryption key is: ekey = HMAC_SHA512(Master-Key, 0x10) + * The authentication key is: akey = HMAC_SHA512(Master-Key, 0x11) + */ + g_eli_crypto_hmac(mkey, G_ELI_MAXKEYLEN, "\x10", 1, sc->sc_ekey, 0); + g_eli_crypto_hmac(mkey, G_ELI_MAXKEYLEN, "\x11", 1, sc->sc_akey, 0); + } + +} +#endif diff --git a/sys/modules/geom/geom_eli/Makefile b/sys/modules/geom/geom_eli/Makefile index e94fb88307b7..7b4c506de62f 100644 --- a/sys/modules/geom/geom_eli/Makefile +++ b/sys/modules/geom/geom_eli/Makefile @@ -3,7 +3,14 @@ .PATH: ${.CURDIR}/../../../geom/eli KMOD= geom_eli -SRCS= g_eli.c g_eli_crypto.c g_eli_ctl.c g_eli_key.c pkcs5v2.c vnode_if.h +SRCS= g_eli.c +SRCS+= g_eli_crypto.c +SRCS+= g_eli_ctl.c +SRCS+= g_eli_integrity.c +SRCS+= g_eli_key.c +SRCS+= g_eli_privacy.c +SRCS+= pkcs5v2.c +SRCS+= vnode_if.h WARNS?= 2 .include diff --git a/tools/regression/geom_eli/init.t b/tools/regression/geom_eli/init.t index 5e4775c846bb..10fd7f741513 100644 --- a/tools/regression/geom_eli/init.t +++ b/tools/regression/geom_eli/init.t @@ -4,11 +4,9 @@ base=`basename $0` no=45 sectors=100 -rnd=`mktemp /tmp/$base.XXXXXX` || exit 1 keyfile=`mktemp /tmp/$base.XXXXXX` || exit 1 -mdconfig -a -t malloc -s `expr $sectors + 1` -u $no || exit 1 -echo "1..36" +echo "1..180" i=1 for cipher in aes:0 aes:128 aes:192 aes:256 \ @@ -16,36 +14,43 @@ for cipher in aes:0 aes:128 aes:192 aes:256 \ blowfish:0 blowfish:128 blowfish:160 blowfish:192 blowfish:224 \ blowfish:256 blowfish:288 blowfish:320 blowfish:352 blowfish:384 \ blowfish:416 blowfish:448; do - algo=${cipher%%:*} + ealgo=${cipher%%:*} keylen=${cipher##*:} + for secsize in 512 1024 2048 4096 8192; do + rnd=`mktemp /tmp/$base.XXXXXX` || exit 1 + mdconfig -a -t malloc -s `expr $secsize \* $sectors + 512`b -u $no || exit 1 - dd if=/dev/random of=${rnd} bs=512 count=${sectors} >/dev/null 2>&1 - dd if=/dev/random of=${keyfile} bs=512 count=16 >/dev/null 2>&1 + dd if=/dev/random of=${keyfile} bs=512 count=16 >/dev/null 2>&1 - geli init -a $algo -l $keylen -P -K $keyfile md${no} - geli attach -p -k $keyfile md${no} + geli init -e $ealgo -l $keylen -P -K $keyfile -s $secsize md${no} + geli attach -p -k $keyfile md${no} - dd if=${rnd} of=/dev/md${no}.eli bs=512 count=${sectors} 2>/dev/null + secs=`diskinfo /dev/md${no}.eli | awk '{print $4}'` - md_rnd=`dd if=${rnd} bs=512 count=${sectors} 2>/dev/null | md5` - md_ddev=`dd if=/dev/md${no}.eli bs=512 count=${sectors} 2>/dev/null | md5` - md_edev=`dd if=/dev/md${no} bs=512 count=${sectors} 2>/dev/null | md5` + dd if=/dev/random of=${rnd} bs=${secsize} count=${secs} >/dev/null 2>&1 + dd if=${rnd} of=/dev/md${no}.eli bs=${secsize} count=${secs} 2>/dev/null - if [ ${md_rnd} = ${md_ddev} ]; then - echo "ok $i - ${cipher}" - else - echo "not ok $i - ${cipher}" - fi - i=$((i+1)) - if [ ${md_rnd} != ${md_edev} ]; then - echo "ok $i - ${cipher}" - else - echo "not ok $i - ${cipher}" - fi - i=$((i+1)) + md_rnd=`dd if=${rnd} bs=${secsize} count=${secs} 2>/dev/null | md5` + md_ddev=`dd if=/dev/md${no}.eli bs=${secsize} count=${secs} 2>/dev/null | md5` + md_edev=`dd if=/dev/md${no} bs=${secsize} count=${secs} 2>/dev/null | md5` - geli detach md${no} + if [ ${md_rnd} = ${md_ddev} ]; then + echo "ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}" + else + echo "not ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}" + fi + i=$((i+1)) + if [ ${md_rnd} != ${md_edev} ]; then + echo "ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}" + else + echo "not ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}" + fi + i=$((i+1)) + + geli detach md${no} + rm -f $rnd + mdconfig -d -u $no + done done -mdconfig -d -u $no -rm -f $rnd $keyfile +rm -f $keyfile diff --git a/tools/regression/geom_eli/onetime.t b/tools/regression/geom_eli/onetime.t index d52ef5640360..70b547966912 100644 --- a/tools/regression/geom_eli/onetime.t +++ b/tools/regression/geom_eli/onetime.t @@ -4,10 +4,8 @@ base=`basename $0` no=45 sectors=100 -rnd=`mktemp /tmp/$base.XXXXXX` || exit 1 -mdconfig -a -t malloc -s $sectors -u $no || exit 1 -echo "1..36" +echo "1..180" i=1 for cipher in aes:0 aes:128 aes:192 aes:256 \ @@ -15,34 +13,38 @@ for cipher in aes:0 aes:128 aes:192 aes:256 \ blowfish:0 blowfish:128 blowfish:160 blowfish:192 blowfish:224 \ blowfish:256 blowfish:288 blowfish:320 blowfish:352 blowfish:384 \ blowfish:416 blowfish:448; do - algo=${cipher%%:*} + ealgo=${cipher%%:*} keylen=${cipher##*:} + for secsize in 512 1024 2048 4096 8192; do + rnd=`mktemp /tmp/$base.XXXXXX` || exit 1 + mdconfig -a -t malloc -s `expr $secsize \* $sectors`b -u $no || exit 1 - dd if=/dev/random of=${rnd} bs=512 count=${sectors} >/dev/null 2>&1 + geli onetime -e $ealgo -l $keylen -s $secsize md${no} - geli onetime -a $algo -l $keylen md${no} + secs=`diskinfo /dev/md${no}.eli | awk '{print $4}'` - dd if=${rnd} of=/dev/md${no}.eli bs=512 count=${sectors} 2>/dev/null + dd if=/dev/random of=${rnd} bs=${secsize} count=${secs} >/dev/null 2>&1 + dd if=${rnd} of=/dev/md${no}.eli bs=${secsize} count=${secs} 2>/dev/null - md_rnd=`dd if=${rnd} bs=512 count=${sectors} 2>/dev/null | md5` - md_ddev=`dd if=/dev/md${no}.eli bs=512 count=${sectors} 2>/dev/null | md5` - md_edev=`dd if=/dev/md${no} bs=512 count=${sectors} 2>/dev/null | md5` + md_rnd=`dd if=${rnd} bs=${secsize} count=${secs} 2>/dev/null | md5` + md_ddev=`dd if=/dev/md${no}.eli bs=${secsize} count=${secs} 2>/dev/null | md5` + md_edev=`dd if=/dev/md${no} bs=${secsize} count=${secs} 2>/dev/null | md5` - if [ ${md_rnd} = ${md_ddev} ]; then - echo "ok $i - ${cipher}" - else - echo "not ok $i - ${cipher}" - fi - i=$((i+1)) - if [ ${md_rnd} != ${md_edev} ]; then - echo "ok $i - ${cipher}" - else - echo "not ok $i - ${cipher}" - fi - i=$((i+1)) + if [ ${md_rnd} = ${md_ddev} ]; then + echo "ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}" + else + echo "not ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}" + fi + i=$((i+1)) + if [ ${md_rnd} != ${md_edev} ]; then + echo "ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}" + else + echo "not ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}" + fi + i=$((i+1)) - geli detach md${no} + geli detach md${no} + rm -f $rnd + mdconfig -d -u $no + done done - -mdconfig -d -u $no -rm -f $rnd