2010-11-08 07:02:45 +03:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
systemd 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 .
systemd 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 systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <string.h>
2010-11-12 02:39:17 +03:00
# include <errno.h>
2010-11-16 05:23:52 +03:00
# include <sys/mman.h>
2010-11-12 02:39:17 +03:00
# include <libcryptsetup.h>
2010-11-19 01:34:42 +03:00
# include <libudev.h>
2010-11-08 07:02:45 +03:00
# include "log.h"
# include "util.h"
2010-11-12 02:39:17 +03:00
# include "ask-password-api.h"
2010-11-14 03:53:46 +03:00
static const char * opt_type = NULL ; /* LUKS1 or PLAIN */
2010-11-12 02:39:17 +03:00
static char * opt_cipher = NULL ;
2010-11-14 03:53:46 +03:00
static unsigned opt_key_size = 0 ;
2010-11-12 02:39:17 +03:00
static char * opt_hash = NULL ;
2010-11-14 03:53:46 +03:00
static unsigned opt_tries = 0 ;
2010-11-12 02:39:17 +03:00
static bool opt_readonly = false ;
static bool opt_verify = false ;
2010-11-14 03:53:46 +03:00
static usec_t opt_timeout = 0 ;
2010-11-12 02:39:17 +03:00
2010-11-14 04:08:31 +03:00
/* Options Debian's crypttab knows we don't:
offset =
skip =
precheck =
check =
checkargs =
noearly =
loud =
keyscript =
*/
2010-11-12 02:39:17 +03:00
static int parse_one_option ( const char * option ) {
assert ( option ) ;
/* Handled outside of this tool */
2010-11-14 03:53:46 +03:00
if ( streq ( option , " noauto " ) )
2010-11-12 02:39:17 +03:00
return 0 ;
if ( startswith ( option , " cipher= " ) ) {
char * t ;
if ( ! ( t = strdup ( option + 7 ) ) )
return - ENOMEM ;
free ( opt_cipher ) ;
opt_cipher = t ;
} else if ( startswith ( option , " size= " ) ) {
2010-11-14 03:53:46 +03:00
if ( safe_atou ( option + 5 , & opt_key_size ) < 0 ) {
2010-11-12 02:39:17 +03:00
log_error ( " size= parse failure, ignoring. " ) ;
return 0 ;
}
} else if ( startswith ( option , " hash= " ) ) {
char * t ;
if ( ! ( t = strdup ( option + 5 ) ) )
return - ENOMEM ;
free ( opt_hash ) ;
opt_hash = t ;
} else if ( startswith ( option , " tries= " ) ) {
if ( safe_atou ( option + 6 , & opt_tries ) < 0 ) {
log_error ( " tries= parse failure, ignoring. " ) ;
return 0 ;
}
} else if ( streq ( option , " readonly " ) )
opt_readonly = true ;
else if ( streq ( option , " verify " ) )
opt_verify = true ;
2010-11-14 03:53:46 +03:00
else if ( streq ( option , " luks " ) )
opt_type = CRYPT_LUKS1 ;
else if ( streq ( option , " plain " ) | |
streq ( option , " swap " ) | |
streq ( option , " tmp " ) )
opt_type = CRYPT_PLAIN ;
2010-11-12 02:39:17 +03:00
else if ( startswith ( option , " timeout= " ) ) {
2010-11-14 03:53:46 +03:00
if ( parse_usec ( option + 8 , & opt_timeout ) < 0 ) {
2010-11-12 02:39:17 +03:00
log_error ( " timeout= parse failure, ignoring. " ) ;
return 0 ;
}
} else
log_error ( " Encountered unknown /etc/crypttab option '%s', ignoring. " , option ) ;
return 0 ;
}
static int parse_options ( const char * options ) {
char * state ;
char * w ;
size_t l ;
assert ( options ) ;
FOREACH_WORD_SEPARATOR ( w , l , options , " , " , state ) {
char * o ;
int r ;
if ( ! ( o = strndup ( w , l ) ) )
return - ENOMEM ;
r = parse_one_option ( o ) ;
free ( o ) ;
if ( r < 0 )
return r ;
}
return 0 ;
}
static void log_glue ( int level , const char * msg , void * usrptr ) {
2010-11-14 03:53:46 +03:00
log_debug ( " %s " , msg ) ;
2010-11-12 02:39:17 +03:00
}
2010-11-08 07:02:45 +03:00
2010-11-19 01:34:42 +03:00
static char * disk_description ( const char * path ) {
struct udev * udev = NULL ;
struct udev_device * device = NULL ;
struct stat st ;
char * description = NULL ;
const char * model ;
assert ( path ) ;
if ( stat ( path , & st ) < 0 )
return NULL ;
if ( ! S_ISBLK ( st . st_mode ) )
return NULL ;
if ( ! ( udev = udev_new ( ) ) )
return NULL ;
if ( ! ( device = udev_device_new_from_devnum ( udev , ' b ' , st . st_rdev ) ) )
goto finish ;
if ( ( model = udev_device_get_property_value ( device , " ID_MODEL_FROM_DATABASE " ) ) | |
( model = udev_device_get_property_value ( device , " ID_MODEL " ) ) )
description = strdup ( model ) ;
finish :
if ( device )
udev_device_unref ( device ) ;
if ( udev )
udev_unref ( udev ) ;
return description ;
}
2010-11-08 07:02:45 +03:00
int main ( int argc , char * argv [ ] ) {
2010-11-12 02:39:17 +03:00
int r = EXIT_FAILURE ;
struct crypt_device * cd = NULL ;
2010-11-14 04:01:29 +03:00
char * password = NULL , * truncated_cipher = NULL ;
2010-11-19 01:34:42 +03:00
const char * cipher = NULL , * cipher_mode = NULL , * hash = NULL , * name = NULL ;
char * description = NULL ;
2010-11-08 07:02:45 +03:00
if ( argc < 3 ) {
log_error ( " This program requires at least two arguments. " ) ;
return EXIT_FAILURE ;
}
2010-11-12 03:01:04 +03:00
log_set_target ( LOG_TARGET_AUTO ) ;
2010-11-08 07:02:45 +03:00
log_parse_environment ( ) ;
log_open ( ) ;
2010-11-14 03:53:46 +03:00
if ( streq ( argv [ 1 ] , " attach " ) ) {
2010-11-12 02:39:17 +03:00
uint32_t flags = 0 ;
int k ;
2010-11-14 03:53:46 +03:00
unsigned try ;
2010-11-12 02:39:17 +03:00
const char * key_file = NULL ;
2010-11-14 03:53:46 +03:00
usec_t until ;
2010-11-14 04:01:29 +03:00
crypt_status_info status ;
2010-11-12 02:39:17 +03:00
if ( argc < 4 ) {
log_error ( " attach requires at least two arguments. " ) ;
goto finish ;
}
2010-11-14 04:08:31 +03:00
if ( argc > = 5 & &
argv [ 4 ] [ 0 ] & &
! streq ( argv [ 4 ] , " - " ) & &
! streq ( argv [ 4 ] , " none " ) ) {
2010-11-12 02:39:17 +03:00
if ( ! path_is_absolute ( argv [ 4 ] ) )
log_error ( " Password file path %s is not absolute. Ignoring. " , argv [ 4 ] ) ;
else
key_file = argv [ 4 ] ;
}
if ( argc > = 6 & & argv [ 5 ] [ 0 ] & & ! streq ( argv [ 5 ] , " - " ) )
parse_options ( argv [ 5 ] ) ;
2010-11-08 07:02:45 +03:00
2010-11-16 05:23:52 +03:00
/* A delicious drop of snake oil */
mlockall ( MCL_FUTURE ) ;
2010-11-19 01:34:42 +03:00
description = disk_description ( argv [ 3 ] ) ;
name = description ? description : argv [ 2 ] ;
2010-11-12 02:39:17 +03:00
if ( ( k = crypt_init ( & cd , argv [ 3 ] ) ) ) {
log_error ( " crypt_init() failed: %s " , strerror ( - k ) ) ;
goto finish ;
}
2010-11-08 07:02:45 +03:00
2010-11-12 02:39:17 +03:00
crypt_set_log_callback ( cd , log_glue , NULL ) ;
2010-11-14 03:53:46 +03:00
status = crypt_status ( cd , argv [ 2 ] ) ;
if ( status = = CRYPT_ACTIVE | | status = = CRYPT_BUSY ) {
log_info ( " Volume %s already active. " , argv [ 2 ] ) ;
r = EXIT_SUCCESS ;
2010-11-12 02:39:17 +03:00
goto finish ;
}
if ( opt_readonly )
flags | = CRYPT_ACTIVATE_READONLY ;
2010-11-14 03:53:46 +03:00
until = now ( CLOCK_MONOTONIC ) + ( opt_timeout > 0 ? opt_timeout : 60 * USEC_PER_SEC ) ;
opt_tries = opt_tries > 0 ? opt_tries : 3 ;
opt_key_size = ( opt_key_size > 0 ? opt_key_size : 256 ) ;
hash = opt_hash ? opt_hash : " ripemd160 " ;
2010-11-14 04:01:29 +03:00
if ( opt_cipher ) {
size_t l ;
l = strcspn ( opt_cipher , " - " ) ;
if ( ! ( truncated_cipher = strndup ( opt_cipher , l ) ) ) {
log_error ( " Out of memory " ) ;
goto finish ;
}
cipher = truncated_cipher ;
cipher_mode = opt_cipher [ l ] ? opt_cipher + l + 1 : " plain " ;
} else {
cipher = " aes " ;
cipher_mode = " cbc-essiv:sha256 " ;
}
2010-11-14 03:53:46 +03:00
for ( try = 0 ; try < opt_tries ; try + + ) {
bool pass_volume_key = false ;
free ( password ) ;
password = NULL ;
if ( ! key_file ) {
2010-11-16 05:24:17 +03:00
char * text ;
2010-11-14 03:53:46 +03:00
2010-11-16 05:47:14 +03:00
if ( asprintf ( & text , " Please enter passphrase for disk %s! " , argv [ 2 ] ) < 0 ) {
2010-11-16 05:24:17 +03:00
log_error ( " Out of memory " ) ;
goto finish ;
}
k = ask_password_auto ( text , " drive-harddisk " , until , & password ) ;
free ( text ) ;
if ( k < 0 ) {
2010-11-14 03:53:46 +03:00
log_error ( " Failed to query password: %s " , strerror ( - k ) ) ;
goto finish ;
}
if ( opt_verify ) {
char * password2 = NULL ;
2010-11-16 05:47:14 +03:00
if ( asprintf ( & text , " Please enter passphrase for disk %s! (verification) " , argv [ 2 ] ) < 0 ) {
2010-11-16 05:24:17 +03:00
log_error ( " Out of memory " ) ;
goto finish ;
}
k = ask_password_auto ( text , " drive-harddisk " , until , & password2 ) ;
free ( text ) ;
if ( k < 0 ) {
2010-11-14 03:53:46 +03:00
log_error ( " Failed to query verification password: %s " , strerror ( - k ) ) ;
goto finish ;
}
if ( ! streq ( password , password2 ) ) {
log_warning ( " Passwords did not match, retrying. " ) ;
free ( password2 ) ;
continue ;
}
free ( password2 ) ;
}
if ( strlen ( password ) + 1 < opt_key_size ) {
char * c ;
/* Pad password if necessary */
if ( ! ( c = new ( char , opt_key_size ) ) ) {
log_error ( " Out of memory. " ) ;
goto finish ;
}
strncpy ( c , password , opt_key_size ) ;
free ( password ) ;
password = c ;
}
}
if ( ! opt_type | | streq ( opt_type , CRYPT_LUKS1 ) )
k = crypt_load ( cd , CRYPT_LUKS1 , NULL ) ;
if ( ( ! opt_type & & k < 0 ) | | streq_ptr ( opt_type , CRYPT_PLAIN ) ) {
struct crypt_params_plain params ;
zero ( params ) ;
params . hash = hash ;
/* In contrast to what the name
* crypt_setup ( ) might suggest this
* doesn ' t actually format anything ,
* it just configures encryption
* parameters when used for plain
* mode . */
k = crypt_format ( cd , CRYPT_PLAIN ,
cipher ,
cipher_mode ,
NULL ,
NULL ,
opt_key_size / 8 ,
& params ) ;
pass_volume_key = streq ( hash , " plain " ) ;
}
if ( k < 0 ) {
log_error ( " Loading of cryptographic parameters failed: %s " , strerror ( - k ) ) ;
goto finish ;
}
log_info ( " Set cipher %s, mode %s, key size %i bits for device %s. " ,
crypt_get_cipher ( cd ) ,
crypt_get_cipher_mode ( cd ) ,
crypt_get_volume_key_size ( cd ) * 8 ,
argv [ 3 ] ) ;
if ( key_file )
k = crypt_activate_by_keyfile ( cd , argv [ 2 ] , CRYPT_ANY_SLOT , key_file , opt_key_size , flags ) ;
else if ( pass_volume_key )
k = crypt_activate_by_volume_key ( cd , argv [ 2 ] , password , opt_key_size , flags ) ;
else
k = crypt_activate_by_passphrase ( cd , argv [ 2 ] , CRYPT_ANY_SLOT , password , strlen ( password ) , flags ) ;
if ( k > = 0 )
break ;
if ( k ! = - EPERM ) {
log_error ( " Failed to activate: %s " , strerror ( - k ) ) ;
goto finish ;
}
log_warning ( " Invalid passphrase. " ) ;
2010-11-12 02:39:17 +03:00
}
2010-11-14 03:53:46 +03:00
if ( try > = opt_tries ) {
log_error ( " Too many attempts. " ) ;
r = EXIT_FAILURE ;
2010-11-12 02:39:17 +03:00
}
} else if ( streq ( argv [ 1 ] , " detach " ) ) {
int k ;
if ( ( k = crypt_init_by_name ( & cd , argv [ 2 ] ) ) ) {
log_error ( " crypt_init() failed: %s " , strerror ( - k ) ) ;
goto finish ;
}
crypt_set_log_callback ( cd , log_glue , NULL ) ;
if ( ( k = crypt_deactivate ( cd , argv [ 2 ] ) ) < 0 ) {
log_error ( " Failed to deactivate: %s " , strerror ( - k ) ) ;
goto finish ;
}
2010-11-08 07:02:45 +03:00
} else {
log_error ( " Unknown verb %s. " , argv [ 1 ] ) ;
goto finish ;
}
2010-11-12 02:39:17 +03:00
r = EXIT_SUCCESS ;
2010-11-08 07:02:45 +03:00
finish :
2010-11-12 02:39:17 +03:00
if ( cd )
crypt_free ( cd ) ;
2010-11-14 03:53:46 +03:00
free ( opt_cipher ) ;
free ( opt_hash ) ;
2010-11-14 04:01:29 +03:00
free ( truncated_cipher ) ;
2010-11-14 03:53:46 +03:00
free ( password ) ;
2010-11-19 01:34:42 +03:00
free ( description ) ;
2010-11-08 07:02:45 +03:00
return r ;
}