Created
December 8, 2013 18:56
-
-
Save rtreffer/7862192 to your computer and use it in GitHub Desktop.
A simple libsshd server that will call luksOpen if you type in a password... Easy remote disk unlocking.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* A simplistic ssh daemon that will try to unlock one or more disks, | |
* simply login as luks and provide your luks password. | |
* This daemon should be part of your initramfs, enabling "headless" | |
* decryption of your disks. You'll usually want to link this binary | |
* statically, merging all dependencies into one opaque file. | |
* | |
* You have to have a valid network setup. You'll otherwise just block your | |
* servers boot sequence. | |
* | |
* This server was heavily inspired by | |
* https://github.com/substack/libssh/blob/master/examples/samplesshd.c | |
* | |
* Build with | |
* gcc -static server.c \ | |
* /usr/lib/x86_64-linux-gnu/libssh.a \ | |
* /usr/lib/x86_64-linux-gnu/libcrypto.a \ | |
* /usr/lib/x86_64-linux-gnu/libz.a /usr/lib/x86_64-linux-gnu/libdl.a \ | |
* -o luks-sshd | |
*/ | |
// #include "config.h" | |
#include <libssh/libssh.h> | |
#include <libssh/server.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <stdio.h> | |
#include <argp.h> | |
#define KEYS_FOLDER "/etc/ssh/" | |
#define MAX_DISKS 256 | |
#define MAX_CMD 2048 | |
static int unlocked = 0; | |
static int diskc = 0; | |
static char *diskv[MAX_DISKS]; | |
static int verbose = 0; | |
static int auth_password(const char *user, const char *password) { | |
char cmd[MAX_CMD]; | |
int i; | |
FILE *proc; | |
if(strcmp(user,"luks")) | |
return 0; | |
// try to unlock the disks | |
for(i = 0; i < diskc; i++) { | |
snprintf(cmd, MAX_CMD - 1, "/sbin/cryptsetup luksOpen '%s' 'luks-sshd-%i'", diskv[i], i); | |
proc = popen(cmd, "w"); | |
fprintf(proc, "%s\n", password); | |
if (pclose(proc) == 0) | |
unlocked++; | |
} | |
if (unlocked != diskc) | |
return 0; | |
return 1; | |
} | |
const char *argp_program_version = "luks-sshd server 0.1" | |
SSH_STRINGIFY(LIBSSH_VERSION); | |
const char *argp_program_bug_address = "<[email protected]>"; | |
/* Program documentation. */ | |
static char doc[] = "luks-sshd - a simple sshd that will unlock luks formatted disks"; | |
/* A description of the arguments we accept. */ | |
static char args_doc[] = "BINDADDR"; | |
/* The options we understand. */ | |
static struct argp_option options[] = { | |
{ | |
.name = "port", | |
.key = 'p', | |
.arg = "PORT", | |
.flags = 0, | |
.doc = "Set the port to bind.", | |
.group = 0 | |
}, | |
{ | |
.name = "hostkey", | |
.key = 'k', | |
.arg = "FILE", | |
.flags = 0, | |
.doc = "Set the host key.", | |
.group = 0 | |
}, | |
{ | |
.name = "dsakey", | |
.key = 'd', | |
.arg = "FILE", | |
.flags = 0, | |
.doc = "Set the dsa key.", | |
.group = 0 | |
}, | |
{ | |
.name = "rsakey", | |
.key = 'r', | |
.arg = "FILE", | |
.flags = 0, | |
.doc = "Set the rsa key.", | |
.group = 0 | |
}, | |
{ | |
.name = "verbose", | |
.key = 'v', | |
.arg = NULL, | |
.flags = 0, | |
.doc = "Get verbose output.", | |
.group = 0 | |
}, | |
{ | |
.name = "luksOpen", | |
.key = 'l', | |
.arg = "DEVICE", | |
.flags = 0, | |
.doc = "Specify the disk to unlock.", | |
.group = 0 | |
}, | |
{NULL, 0, 0, 0, NULL, 0} | |
}; | |
/* Parse a single option. */ | |
static error_t parse_opt (int key, char *arg, struct argp_state *state) { | |
/* Get the input argument from argp_parse, which we | |
* know is a pointer to our arguments structure. | |
*/ | |
ssh_bind sshbind = state->input; | |
switch (key) { | |
case 'p': | |
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg); | |
break; | |
case 'd': | |
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg); | |
break; | |
case 'k': | |
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg); | |
break; | |
case 'r': | |
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg); | |
break; | |
case 'v': | |
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "3"); | |
verbose++; | |
break; | |
case 'l': | |
diskv[diskc++] = arg; | |
diskc %= MAX_DISKS; | |
break; | |
case ARGP_KEY_ARG: | |
if (state->arg_num >= 1) { | |
/* Too many arguments. */ | |
argp_usage (state); | |
} | |
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg); | |
break; | |
case ARGP_KEY_END: | |
if (state->arg_num < 1) { | |
/* Not enough arguments. */ | |
argp_usage (state); | |
} | |
break; | |
default: | |
return ARGP_ERR_UNKNOWN; | |
} | |
return 0; | |
} | |
/* Our argp parser. */ | |
static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL}; | |
int main(int argc, char **argv){ | |
ssh_session session; | |
ssh_bind sshbind; | |
ssh_message message; | |
ssh_channel chan=0; | |
char buf[2048]; | |
int auth=0; | |
int sftp=0; | |
int i; | |
int r; | |
sshbind=ssh_bind_new(); | |
session=ssh_new(); | |
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, KEYS_FOLDER "ssh_host_dsa_key"); | |
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, KEYS_FOLDER "ssh_host_rsa_key"); | |
argp_parse (&argp, argc, argv, 0, 0, sshbind); | |
if(ssh_bind_listen(sshbind)<0){ | |
printf("Error listening to socket: %s\n",ssh_get_error(sshbind)); | |
return 1; | |
} | |
r=ssh_bind_accept(sshbind,session); | |
if(r==SSH_ERROR){ | |
printf("error accepting a connection : %s\n",ssh_get_error(sshbind)); | |
return 1; | |
} | |
if (ssh_handle_key_exchange(session)) { | |
printf("ssh_handle_key_exchange: %s\n", ssh_get_error(session)); | |
return 1; | |
} | |
do { | |
do { | |
message=ssh_message_get(session); | |
if(!message) | |
break; | |
switch(ssh_message_type(message)){ | |
case SSH_REQUEST_AUTH: | |
switch(ssh_message_subtype(message)){ | |
case SSH_AUTH_METHOD_PASSWORD: | |
if(auth_password(ssh_message_auth_user(message), | |
ssh_message_auth_password(message))){ | |
auth=1; | |
ssh_message_auth_reply_success(message,0); | |
break; | |
} | |
// not authenticated, send default message | |
case SSH_AUTH_METHOD_NONE: | |
default: | |
ssh_message_auth_set_methods(message,SSH_AUTH_METHOD_PASSWORD); | |
ssh_message_reply_default(message); | |
break; | |
} | |
break; | |
default: | |
ssh_message_reply_default(message); | |
} | |
ssh_message_free(message); | |
} while (!auth); | |
// we imediatly disconnect everyone. | |
ssh_disconnect(session); | |
} while (!auth); // repeat if we could not unlock everything | |
ssh_bind_free(sshbind); | |
ssh_finalize(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment