/* * LUKS - Linux Unified Key Setup * * Copyright (C) 2004-2006, Clemens Fruhwirth * * 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 published by the Free Software Foundation. * * 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 */ #include #include #include #include #include #include #include #include #include #include #include #include "luks.h" #include "af.h" #include "pbkdf.h" #include "random.h" #include #include <../lib/internal.h> #define div_round_up(a,b) ({ \ typeof(a) __a = (a); \ typeof(b) __b = (b); \ (__a - 1) / __b + 1; \ }) static inline int round_up_modulo(int x, int m) { return div_round_up(x, m) * m; } struct luks_masterkey *LUKS_alloc_masterkey(int keylength) { struct luks_masterkey *mk=malloc(sizeof(*mk) + keylength); if(NULL == mk) return NULL; mk->keyLength=keylength; return mk; } void LUKS_dealloc_masterkey(struct luks_masterkey *mk) { if(NULL != mk) { memset(mk->key,0,mk->keyLength); mk->keyLength=0; free(mk); } } struct luks_masterkey *LUKS_generate_masterkey(int keylength) { struct luks_masterkey *mk=LUKS_alloc_masterkey(keylength); if(NULL == mk) return NULL; int r = getRandom(mk->key,keylength); if(r < 0) { LUKS_dealloc_masterkey(mk); return NULL; } return mk; } int LUKS_read_phdr(const char *device, struct luks_phdr *hdr) { int devfd = 0, r = 0; unsigned int i; uint64_t size; char luksMagic[] = LUKS_MAGIC; devfd = open(device,O_RDONLY | O_DIRECT | O_SYNC); if(-1 == devfd) { set_error(_("Can't open device: %s\n"), device); return -EINVAL; } if(read_blockwise(devfd, hdr, sizeof(struct luks_phdr)) < sizeof(struct luks_phdr)) { r = -EIO; } else if(memcmp(hdr->magic, luksMagic, LUKS_MAGIC_L)) { /* Check magic */ set_error(_("%s is not a LUKS partition\n"), device); r = -EINVAL; } else if((hdr->version = ntohs(hdr->version)) != 1) { /* Convert every uint16/32_t item from network byte order */ set_error(_("unknown LUKS version %d\n"), hdr->version); r = -EINVAL; } else if (PBKDF2_HMAC_ready(hdr->hashSpec) < 0) { set_error(_("Requested LUKS hash %s is not supported.\n"), hdr->hashSpec); r = -EINVAL; } else { hdr->payloadOffset = ntohl(hdr->payloadOffset); hdr->keyBytes = ntohl(hdr->keyBytes); hdr->mkDigestIterations = ntohl(hdr->mkDigestIterations); for(i = 0; i < LUKS_NUMKEYS; ++i) { hdr->keyblock[i].active = ntohl(hdr->keyblock[i].active); hdr->keyblock[i].passwordIterations = ntohl(hdr->keyblock[i].passwordIterations); hdr->keyblock[i].keyMaterialOffset = ntohl(hdr->keyblock[i].keyMaterialOffset); hdr->keyblock[i].stripes = ntohl(hdr->keyblock[i].stripes); } } #ifdef BLKGETSIZE64 if (ioctl(devfd, BLKGETSIZE64, &size) < 0 || size < (uint64_t)hdr->payloadOffset) { set_error(_("LUKS header detected but device %s is too small.\n"), device); r = -EINVAL; } #endif close(devfd); return r; } int LUKS_write_phdr(const char *device, struct luks_phdr *hdr) { int devfd = 0; unsigned int i; struct luks_phdr convHdr; int r; devfd = open(device,O_RDWR | O_DIRECT | O_SYNC); if(-1 == devfd) { set_error(_("Can't open device %s"), device); return -EINVAL; } memcpy(&convHdr, hdr, sizeof(struct luks_phdr)); memset(&convHdr._padding, 0, sizeof(convHdr._padding)); /* Convert every uint16/32_t item to network byte order */ convHdr.version = htons(hdr->version); convHdr.payloadOffset = htonl(hdr->payloadOffset); convHdr.keyBytes = htonl(hdr->keyBytes); convHdr.mkDigestIterations = htonl(hdr->mkDigestIterations); for(i = 0; i < LUKS_NUMKEYS; ++i) { convHdr.keyblock[i].active = htonl(hdr->keyblock[i].active); convHdr.keyblock[i].passwordIterations = htonl(hdr->keyblock[i].passwordIterations); convHdr.keyblock[i].keyMaterialOffset = htonl(hdr->keyblock[i].keyMaterialOffset); convHdr.keyblock[i].stripes = htonl(hdr->keyblock[i].stripes); } r = write_blockwise(devfd, &convHdr, sizeof(struct luks_phdr)) < sizeof(struct luks_phdr) ? -EIO : 0; close(devfd); return r; } int LUKS_generate_phdr(struct luks_phdr *header, const struct luks_masterkey *mk, const char *cipherName, const char *cipherMode, const char *hashSpec, unsigned int stripes, unsigned int alignPayload) { unsigned int i=0; unsigned int blocksPerStripeSet = div_round_up(mk->keyLength*stripes,SECTOR_SIZE); int r; char luksMagic[] = LUKS_MAGIC; uuid_t partitionUuid; int currentSector; int alignSectors = 4096/SECTOR_SIZE; if (alignPayload == 0) alignPayload = alignSectors; memset(header,0,sizeof(struct luks_phdr)); /* Set Magic */ memcpy(header->magic,luksMagic,LUKS_MAGIC_L); header->version=1; strncpy(header->cipherName,cipherName,LUKS_CIPHERNAME_L); strncpy(header->cipherMode,cipherMode,LUKS_CIPHERMODE_L); strncpy(header->hashSpec,hashSpec,LUKS_HASHSPEC_L); header->keyBytes=mk->keyLength; r = getRandom(header->mkDigestSalt,LUKS_SALTSIZE); if(r < 0) { set_error( _("Cannot create LUKS header: reading random salt failed.")); return r; } /* Compute master key digest */ header->mkDigestIterations = LUKS_MKD_ITER; r = PBKDF2_HMAC(header->hashSpec,mk->key,mk->keyLength, header->mkDigestSalt,LUKS_SALTSIZE, header->mkDigestIterations, header->mkDigest,LUKS_DIGESTSIZE); if(r < 0) { set_error( _("Cannot create LUKS header: header digest failed (using hash %s)."), header->hashSpec); return r; } currentSector = round_up_modulo(LUKS_PHDR_SIZE, alignSectors); for(i = 0; i < LUKS_NUMKEYS; ++i) { header->keyblock[i].active = LUKS_KEY_DISABLED; header->keyblock[i].keyMaterialOffset = currentSector; header->keyblock[i].stripes = stripes; currentSector = round_up_modulo(currentSector + blocksPerStripeSet, alignSectors); } currentSector = round_up_modulo(currentSector, alignPayload); header->payloadOffset=currentSector; uuid_generate(partitionUuid); uuid_unparse(partitionUuid, header->uuid); return 0; } int LUKS_set_key(const char *device, unsigned int keyIndex, const char *password, size_t passwordLen, struct luks_phdr *hdr, struct luks_masterkey *mk, struct setup_backend *backend) { char derivedKey[hdr->keyBytes]; char *AfKey; unsigned int AFEKSize; int r; if(hdr->keyblock[keyIndex].active != LUKS_KEY_DISABLED) { set_error( _("key %d active, purge first"), keyIndex); return -EINVAL; } if(hdr->keyblock[keyIndex].stripes < LUKS_STRIPES) { set_error(_("key material section %d includes too few stripes. Header manipulation?"),keyIndex); return -EINVAL; } r = getRandom(hdr->keyblock[keyIndex].passwordSalt, LUKS_SALTSIZE); if(r < 0) return r; // assert((mk->keyLength % TWOFISH_BLOCKSIZE) == 0); FIXME r = PBKDF2_HMAC(hdr->hashSpec, password,passwordLen, hdr->keyblock[keyIndex].passwordSalt,LUKS_SALTSIZE, hdr->keyblock[keyIndex].passwordIterations, derivedKey, hdr->keyBytes); if(r < 0) return r; /* * AF splitting, the masterkey stored in mk->key is splitted to AfMK */ AFEKSize = hdr->keyblock[keyIndex].stripes*mk->keyLength; AfKey = (char *)malloc(AFEKSize); if(AfKey == NULL) return -ENOMEM; r = AF_split(mk->key,AfKey,mk->keyLength,hdr->keyblock[keyIndex].stripes,hdr->hashSpec); if(r < 0) goto out; /* Encryption via dm */ r = LUKS_encrypt_to_storage(AfKey, AFEKSize, hdr, derivedKey, hdr->keyBytes, device, hdr->keyblock[keyIndex].keyMaterialOffset, backend); if(r < 0) { if(!get_error()) set_error("Failed to write to key storage"); goto out; } /* Mark the key as active in phdr */ hdr->keyblock[keyIndex].active = LUKS_KEY_ENABLED; r = LUKS_write_phdr(device,hdr); if(r < 0) goto out; r = 0; out: free(AfKey); return r; } /* Try to open a particular key slot */ int LUKS_open_key(const char *device, unsigned int keyIndex, const char *password, size_t passwordLen, struct luks_phdr *hdr, struct luks_masterkey *mk, struct setup_backend *backend) { char derivedKey[hdr->keyBytes]; char *AfKey; size_t AFEKSize; char checkHashBuf[LUKS_DIGESTSIZE]; int r; if(hdr->keyblock[keyIndex].active != LUKS_KEY_ENABLED) { return -ENOENT; } // assert((mk->keyLength % TWOFISH_BLOCKSIZE) == 0); FIXME AFEKSize = hdr->keyblock[keyIndex].stripes*mk->keyLength; AfKey = (char *)malloc(AFEKSize); if(AfKey == NULL) return -ENOMEM; r = PBKDF2_HMAC(hdr->hashSpec, password,passwordLen, hdr->keyblock[keyIndex].passwordSalt,LUKS_SALTSIZE, hdr->keyblock[keyIndex].passwordIterations, derivedKey, hdr->keyBytes); if(r < 0) goto out; r = LUKS_decrypt_from_storage(AfKey, AFEKSize, hdr, derivedKey, hdr->keyBytes, device, hdr->keyblock[keyIndex].keyMaterialOffset, backend); if(r < 0) goto out; r = AF_merge(AfKey,mk->key,mk->keyLength,hdr->keyblock[keyIndex].stripes,hdr->hashSpec); if(r < 0) goto out; r = PBKDF2_HMAC(hdr->hashSpec,mk->key,mk->keyLength, hdr->mkDigestSalt,LUKS_SALTSIZE, hdr->mkDigestIterations, checkHashBuf,LUKS_DIGESTSIZE); if(r < 0) goto out; r = (memcmp(checkHashBuf,hdr->mkDigest, LUKS_DIGESTSIZE) == 0)?0:-EPERM; out: free(AfKey); if( r < 0 && !get_error()) set_error("Failed to read from key storage."); return r; } /* Tries to open any key from a given LUKS device reading the header on its own */ int LUKS_open_any_key(const char *device, const char *password, size_t passwordLen, struct luks_phdr *hdr, struct luks_masterkey **mk, struct setup_backend *backend) { int r; r = LUKS_read_phdr(device, hdr); if(r < 0) return r; return LUKS_open_any_key_with_hdr(device,password,passwordLen,hdr,mk,backend); } int LUKS_open_any_key_with_hdr(const char *device, const char *password, size_t passwordLen, struct luks_phdr *hdr, struct luks_masterkey **mk, struct setup_backend *backend) { unsigned int i; int r; *mk=LUKS_alloc_masterkey(hdr->keyBytes); for(i=0; i= 0 && i < 5) getRandom(buffer, bufLen); else if(i >= 5 && i < 32) wipeSpecial(buffer, bufLen, i - 5); else if(i >= 32 && i < 38) getRandom(buffer, bufLen); else if(i >= 38 && i < 39) memset(buffer, 0xFF, bufLen); if(write_lseek_blockwise(devfd, buffer, bufLen, from * SECTOR_SIZE) < 0) { r = -EIO; break; } } free(buffer); close(devfd); return r; } int LUKS_del_key(const char *device, unsigned int keyIndex) { struct luks_phdr hdr; unsigned int startOffset, endOffset, stripesLen; int r; r = LUKS_read_phdr(device, &hdr); if(r != 0) { /* placeholder */ } else if(keyIndex >= LUKS_NUMKEYS || hdr.keyblock[keyIndex].active != LUKS_KEY_ENABLED) { set_error(_("Key %d not active. Can't wipe.\n"), keyIndex); r = -ENOENT; } else { /* secure deletion of key material */ startOffset = hdr.keyblock[keyIndex].keyMaterialOffset; stripesLen = hdr.keyBytes * hdr.keyblock[keyIndex].stripes; endOffset = startOffset + div_round_up(stripesLen, SECTOR_SIZE); r = wipe(device, startOffset, endOffset); if(r == 0) { /* mark the key as inactive in header */ hdr.keyblock[keyIndex].active = LUKS_KEY_DISABLED; r = LUKS_write_phdr(device, &hdr); } } return r; } int LUKS_is_last_keyslot(const char *device, unsigned int keyIndex) { struct luks_phdr hdr; unsigned int i; int r; r = LUKS_read_phdr(device, &hdr); if(r < 0) return r; for(i = 0; i < LUKS_NUMKEYS; i++) { if(i != keyIndex && hdr.keyblock[i].active == LUKS_KEY_ENABLED) return 0; } return 1; } int LUKS_benchmarkt_iterations(const char *hash, unsigned int *count) { if (PBKDF2_performance_check(hash, count) < 0) { set_error(_("Not compatible options (using hash algorithm %s)."), hash); return -EINVAL; } *count /= 2; return 0; } int LUKS_device_ready(const char *device, int mode) { int devfd; struct stat st; if(stat(device, &st) < 0) { set_error(_("Device %s doesn't exist or access denied."), device); return 0; } devfd = open(device, mode | O_DIRECT | O_SYNC); if(devfd < 0) { set_error(_("Can't open device %s for %s%saccess."), device, (mode & O_EXCL)?_("exclusive "):"", (mode & O_RDWR)?_("writable "):"read-only "); return 0; } close(devfd); return 1; }