Changeset View
Standalone View
sbin/geom/class/eli/geom_eli.c
Show First 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | |||||
#include "misc/subr.h" | #include "misc/subr.h" | ||||
uint32_t lib_version = G_LIB_VERSION; | uint32_t lib_version = G_LIB_VERSION; | ||||
uint32_t version = G_ELI_VERSION; | uint32_t version = G_ELI_VERSION; | ||||
#define GELI_BACKUP_DIR "/var/backups/" | #define GELI_BACKUP_DIR "/var/backups/" | ||||
#define GELI_ENC_ALGO "aes" | #define GELI_ENC_ALGO "aes" | ||||
#define BUFSIZE 1024 | |||||
/* | |||||
* Passphrase cached when attaching multiple providers, in order to be more | |||||
* user-friendly if they are using the same passphrase. | |||||
*/ | |||||
static char cached_passphrase[BUFSIZE] = ""; | |||||
static void eli_main(struct gctl_req *req, unsigned flags); | static void eli_main(struct gctl_req *req, unsigned flags); | ||||
static void eli_init(struct gctl_req *req); | static void eli_init(struct gctl_req *req); | ||||
static void eli_attach(struct gctl_req *req); | static void eli_attach(struct gctl_req *req); | ||||
static void eli_configure(struct gctl_req *req); | static void eli_configure(struct gctl_req *req); | ||||
static void eli_setkey(struct gctl_req *req); | static void eli_setkey(struct gctl_req *req); | ||||
static void eli_delkey(struct gctl_req *req); | static void eli_delkey(struct gctl_req *req); | ||||
static void eli_resume(struct gctl_req *req); | static void eli_resume(struct gctl_req *req); | ||||
static void eli_kill(struct gctl_req *req); | static void eli_kill(struct gctl_req *req); | ||||
static void eli_backup(struct gctl_req *req); | static void eli_backup(struct gctl_req *req); | ||||
static void eli_restore(struct gctl_req *req); | static void eli_restore(struct gctl_req *req); | ||||
static void eli_resize(struct gctl_req *req); | static void eli_resize(struct gctl_req *req); | ||||
static void eli_version(struct gctl_req *req); | static void eli_version(struct gctl_req *req); | ||||
static void eli_clear(struct gctl_req *req); | static void eli_clear(struct gctl_req *req); | ||||
static void eli_dump(struct gctl_req *req); | static void eli_dump(struct gctl_req *req); | ||||
static int eli_backup_create(struct gctl_req *req, const char *prov, | static int eli_backup_create(struct gctl_req *req, const char *prov, | ||||
const char *file); | const char *file); | ||||
/* | /* | ||||
* Available commands: | * Available commands: | ||||
* | * | ||||
* init [-bdgPTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov | * init [-bdgPTv] [-a aalgo] [-B backupfile] [-e ealgo] [-i iterations] [-l keylen] [-J newpassfile] [-K newkeyfile] [-s sectorsize] [-V version] prov | ||||
* label - alias for 'init' | * label - alias for 'init' | ||||
* attach [-dprv] [-j passfile] [-k keyfile] prov | * attach [-dprv] [-j passfile] [-k keyfile] prov ... | ||||
* detach [-fl] prov ... | * detach [-fl] prov ... | ||||
* stop - alias for 'detach' | * stop - alias for 'detach' | ||||
* onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov | * onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov | ||||
* configure [-bBgGtT] prov ... | * configure [-bBgGtT] prov ... | ||||
* setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov | * setkey [-pPv] [-n keyno] [-j passfile] [-J newpassfile] [-k keyfile] [-K newkeyfile] prov | ||||
* delkey [-afv] [-n keyno] prov | * delkey [-afv] [-n keyno] prov | ||||
* suspend [-v] -a | prov ... | * suspend [-v] -a | prov ... | ||||
* resume [-pv] [-j passfile] [-k keyfile] prov | * resume [-pv] [-j passfile] [-k keyfile] prov | ||||
▲ Show 20 Lines • Show All 170 Lines • ▼ Show 20 Lines | struct g_command class_commands[] = { | ||||
{ "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, | { "dump", G_FLAG_VERBOSE, eli_main, G_NULL_OPTS, | ||||
"[-v] prov ..." | "[-v] prov ..." | ||||
}, | }, | ||||
G_CMD_SENTINEL | G_CMD_SENTINEL | ||||
}; | }; | ||||
static int verbose = 0; | static int verbose = 0; | ||||
#define BUFSIZE 1024 | |||||
static int | static int | ||||
eli_protect(struct gctl_req *req) | eli_protect(struct gctl_req *req) | ||||
{ | { | ||||
struct rlimit rl; | struct rlimit rl; | ||||
/* Disable core dumps. */ | /* Disable core dumps. */ | ||||
rl.rlim_cur = 0; | rl.rlim_cur = 0; | ||||
rl.rlim_max = 0; | rl.rlim_max = 0; | ||||
▲ Show 20 Lines • Show All 189 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
eli_genkey_passphrase(struct gctl_req *req, struct g_eli_metadata *md, bool new, | eli_genkey_passphrase(struct gctl_req *req, struct g_eli_metadata *md, bool new, | ||||
struct hmac_ctx *ctxp) | struct hmac_ctx *ctxp) | ||||
{ | { | ||||
char passbuf[BUFSIZE]; | char passbuf[BUFSIZE]; | ||||
bool nopassphrase; | bool nopassphrase; | ||||
int nfiles; | int nfiles; | ||||
/* | |||||
* Return error if the 'do not use passphrase' flag was given but a | |||||
wblockUnsubmitted Done Inline Actionswblock: ```* Return error if the 'do not use passphrase' flag is given but a | |||||
* passfile was provided. | |||||
*/ | |||||
nopassphrase = | nopassphrase = | ||||
gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); | gctl_get_int(req, new ? "nonewpassphrase" : "nopassphrase"); | ||||
if (nopassphrase) { | if (nopassphrase) { | ||||
if (gctl_has_param(req, new ? "newpassfile0" : "passfile0")) { | if (gctl_has_param(req, new ? "newpassfile0" : "passfile0")) { | ||||
gctl_error(req, | gctl_error(req, | ||||
"Options -%c and -%c are mutually exclusive.", | "Options -%c and -%c are mutually exclusive.", | ||||
new ? 'J' : 'j', new ? 'P' : 'p'); | new ? 'J' : 'j', new ? 'P' : 'p'); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | |||||
* Return error if using a provider which does not require a passphrase | |||||
Done Inline Actionss/,// wblock: s/,// | |||||
* but the 'do not use passphrase' flag was not given. | |||||
*/ | |||||
if (!new && md->md_iterations == -1) { | if (!new && md->md_iterations == -1) { | ||||
gctl_error(req, "Missing -p flag."); | gctl_error(req, "Missing -p flag."); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
passbuf[0] = '\0'; | passbuf[0] = '\0'; | ||||
/* Use cached passphrase if defined. */ | |||||
if (strcmp(cached_passphrase, "") != 0) { | |||||
sobomaxUnsubmitted Done Inline Actionsstrlen(cached_passphrase) > 0 or cached_passphrase[0] != '\0' sobomax: ```
strlen(cached_passphrase) > 0
```
or
```
cached_passphrase[0] != '\0'
``` | |||||
memcpy(passbuf, cached_passphrase, sizeof(passbuf)); | |||||
sobomaxUnsubmitted Done Inline Actionssrlcpy(passbuf, cached_passphrase, sizeof(passbuf)); sobomax: ```
srlcpy(passbuf, cached_passphrase, sizeof(passbuf));
``` | |||||
} else { | |||||
nfiles = eli_genkey_files(req, new, "passfile", NULL, passbuf, | nfiles = eli_genkey_files(req, new, "passfile", NULL, passbuf, | ||||
sizeof(passbuf)); | sizeof(passbuf)); | ||||
if (nfiles == -1) | if (nfiles == -1) | ||||
return (-1); | return (-1); | ||||
else if (nfiles == 0) { | else if (nfiles == 0) { | ||||
if (eli_genkey_passphrase_prompt(req, new, passbuf, | if (eli_genkey_passphrase_prompt(req, new, passbuf, | ||||
sizeof(passbuf)) == -1) { | sizeof(passbuf)) == -1) { | ||||
return (-1); | return (-1); | ||||
} | } | ||||
} | } | ||||
/* Cache the passphrase for other providers. */ | |||||
memcpy(cached_passphrase, passbuf, sizeof(passbuf)); | |||||
sobomaxUnsubmitted Done Inline Actionssrlcpy(passbuf, cached_passphrase, sizeof(passbuf)); sobomax: ```
srlcpy(passbuf, cached_passphrase, sizeof(passbuf));
``` | |||||
sobomaxUnsubmitted Done Inline ActionsSorry, actually should be (note sizeof() of wrong argument, should be size of the DESTINATION buffer: srlcpy(cached_passphrase, passbuf, sizeof(cached_passphrase)); sobomax: Sorry, actually should be (note sizeof() of wrong argument, should be size of the DESTINATION… | |||||
} | |||||
/* | /* | ||||
* Field md_iterations equal to -1 means "choose some sane | * Field md_iterations equal to -1 means "choose some sane | ||||
* value for me". | * value for me". | ||||
*/ | */ | ||||
if (md->md_iterations == -1) { | if (md->md_iterations == -1) { | ||||
assert(new); | assert(new); | ||||
if (verbose) | if (verbose) | ||||
printf("Calculating number of iterations...\n"); | printf("Calculating number of iterations...\n"); | ||||
▲ Show 20 Lines • Show All 361 Lines • ▼ Show 20 Lines | if (strcmp(backfile, "none") != 0 && | ||||
printf("\n\t# geli restore %s %s\n\n", backfile, prov); | printf("\n\t# geli restore %s %s\n\n", backfile, prov); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
eli_attach(struct gctl_req *req) | eli_attach(struct gctl_req *req) | ||||
{ | { | ||||
struct g_eli_metadata md; | struct g_eli_metadata md; | ||||
unsigned char key[G_ELI_USERKEYLEN]; | |||||
const char *prov; | const char *prov; | ||||
off_t mediasize; | off_t mediasize; | ||||
int nargs; | char param[16]; | ||||
int i, nargs; | |||||
nargs = gctl_get_int(req, "nargs"); | nargs = gctl_get_int(req, "nargs"); | ||||
if (nargs != 1) { | if (nargs == 0) { | ||||
gctl_error(req, "Invalid number of arguments."); | gctl_error(req, "Too few arguments."); | ||||
return; | return; | ||||
} | } | ||||
prov = gctl_get_ascii(req, "arg0"); | |||||
/* Use a 2D array for storing the derived keys (1 row per provider) */ | |||||
unsigned char key[nargs][G_ELI_USERKEYLEN]; | |||||
delphijUnsubmitted Done Inline ActionsWhy do we need an array (versus just doing these operations in one single loop, and discard the key right after each gctl_issue) here? The "Attached to" and "\n" can be done by checking i's value below. The benefit of doing the loop is that we would be able to distinguish individual failures. delphij: Why do we need an array (versus just doing these operations in one single loop, and discard the… | |||||
woodsb02AuthorUnsubmitted Done Inline ActionsThis is because the geom request is passed to the kernel with all of the providers listed on the command line. Without this change the kernel errors out if there is more than one provider listed. I have also submitted an alternative implementation of this change which does not modify the kernel. Instead it creates new child geom requests and issues them to the kernel one-by-one. See D12644. woodsb02: This is because the geom request is passed to the kernel with all of the providers listed on… | |||||
/* Generate the derived key for each provider */ | |||||
for (i = 0; i < nargs; i++) { | |||||
prov = gctl_get_ascii(req, "arg%d", i); | |||||
if (eli_metadata_read(req, prov, &md) == -1) | if (eli_metadata_read(req, prov, &md) == -1) | ||||
return; | return; | ||||
mediasize = g_get_mediasize(prov); | mediasize = g_get_mediasize(prov); | ||||
if (md.md_provsize != (uint64_t)mediasize) { | if (md.md_provsize != (uint64_t)mediasize) { | ||||
gctl_error(req, "Provider size mismatch."); | gctl_error(req, "Provider size mismatch."); | ||||
return; | return; | ||||
} | } | ||||
if (eli_genkey(req, &md, key, false) == NULL) { | if (eli_genkey(req, &md, key[i], false) == NULL) { | ||||
bzero(key, sizeof(key)); | bzero(key, sizeof(key)); | ||||
return; | return; | ||||
} | } | ||||
gctl_ro_param(req, "key", sizeof(key), key); | snprintf(param, sizeof(param), "key%d", i); | ||||
gctl_ro_param(req, param, sizeof(key[i]), key[i]); | |||||
} | |||||
if (gctl_issue(req) == NULL) { | if (gctl_issue(req) == NULL) { | ||||
if (verbose) | if (verbose) { | ||||
printf("Attached to %s.\n", prov); | printf("Attached to"); | ||||
for (i = 0; i < nargs; i++) { | |||||
printf(" %s", gctl_get_ascii(req, "arg%d", i)); | |||||
} | } | ||||
printf(".\n"); | |||||
} | |||||
} | |||||
/* Clear each of the derived keys from the 2D array */ | |||||
bzero(key, sizeof(key)); | bzero(key, sizeof(key)); | ||||
/* Clear the cached passphrase */ | |||||
bzero(cached_passphrase, sizeof(cached_passphrase)); | |||||
sobomaxUnsubmitted Done Inline ActionsNot sure if you need to zero-out the whole buf. cached_passphrase[0] = '\0' might suffice. sobomax: Not sure if you need to zero-out the whole buf.
```
cached_passphrase[0] = '\0'
```
might… | |||||
woodsb02AuthorUnsubmitted Done Inline ActionsI think the entire passphrase should be zeroed out, to prevent it sticking around in memory in clear text. woodsb02: I think the entire passphrase should be zeroed out, to prevent it sticking around in memory in… | |||||
delphijUnsubmitted Done Inline ActionsI agree (if I was you I would use explicit_bzero() here too, but it would be a bug of the compiler if it optimize the bzero() away). delphij: I agree (if I was you I would use explicit_bzero() here too, but it would be a bug of the… | |||||
Done Inline ActionsUnrelated to your change, but 'key' is on stack and therefore explicit_bzero() should be used, or it could be optimized away because key is undefined once out of scope. delphij: Unrelated to your change, but 'key' is on stack and therefore explicit_bzero() should be used… | |||||
Done Inline ActionsThat seems like the right idea. woodsb02: That seems like the right idea.
There are 39 instances of bzero in sbin/geom/class/eli/geom_eli. | |||||
} | } | ||||
static void | static void | ||||
eli_configure_detached(struct gctl_req *req, const char *prov, int boot, | eli_configure_detached(struct gctl_req *req, const char *prov, int boot, | ||||
int geliboot, int displaypass, int trim) | int geliboot, int displaypass, int trim) | ||||
{ | { | ||||
struct g_eli_metadata md; | struct g_eli_metadata md; | ||||
bool changed = 0; | bool changed = 0; | ||||
▲ Show 20 Lines • Show All 841 Lines • Show Last 20 Lines |