Skip to content

Instantly share code, notes, and snippets.

@rtreffer
Created December 8, 2013 18:56
Show Gist options
  • Save rtreffer/7862192 to your computer and use it in GitHub Desktop.
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.
/*
* 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 = "<treffer@measite.de>";
/* 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