=== modified file 'Makefile' --- Makefile 2008-09-03 18:47:50 +0000 +++ Makefile 2008-09-03 19:04:05 +0000 @@ -67,10 +67,21 @@ %.8mandos: %.xml legalnotice.xml $(DOCBOOKTOMAN) -mandos.8: mandos.xml mandos-options.xml - $(DOCBOOKTOMAN) - -mandos.conf.5: mandos.conf.xml mandos-options.xml +mandos.8: mandos.xml mandos-options.xml overview.xml legalnotice.xml + $(DOCBOOKTOMAN) + +mandos-keygen.8: mandos-keygen.xml overview.xml legalnotice.xml + $(DOCBOOKTOMAN) + +mandos.conf.5: mandos.conf.xml mandos-options.xml legalnotice.xml + $(DOCBOOKTOMAN) + +plugin-runner.8mandos: plugin-runner.xml overview.xml legalnotice.xml + $(DOCBOOKTOMAN) + +plugins.d/password-request.8mandos: plugins.d/password-request.xml \ + mandos-options.xml \ + overview.xml legalnotice.xml $(DOCBOOKTOMAN) plugins.d/password-request: plugins.d/password-request.o @@ -92,10 +103,11 @@ check: ./mandos --check -# Run the server with a local key +# Run the client with a local config and key run-client: all keydir/seckey.txt keydir/pubkey.txt \ keydir/secring.gpg keydir/pubring.gpg ./plugin-runner --plugin-dir=plugins.d \ + --config-file=plugin-runner.conf \ --options-for=password-request:--seckey=keydir/seckey.txt,--pubkey=keydir/pubkey.txt # Used by run-client === modified file 'TODO' --- TODO 2008-09-02 10:27:08 +0000 +++ TODO 2008-09-03 17:34:29 +0000 @@ -6,44 +6,8 @@ ** [#B] Add more comments to code ** [#B] Add more if(debug) calls ** [#B] Seperate more code to function for more readability -** [#A] Man page: man8/plugin-runner.8mandos -*** EXAMPLE - Examples of normal usage, debug usage, debugging single or all - plugins, etc. -*** SECURITY - Note the danger of using this program, since you might lock - yourself out of your system without any means of entering the root - file system password. This is, however, very unlikely considering - the fallback to getpass(3). -*** BUGS -*** SEE ALSO - Explaining text on what you can read * password-request -** [#A] Man page: man8/password-request.8mandos -*** SYNOPSIS - Document short options -*** DESCRIPTION - State that this command is not meant to be invoked directly, but - is run as a plugin from mandos-client(8) and only run in the - initrd environment, not the real system. -*** PURPOSE - As in mandos.xml -*** OVERVIEW - As in mandos.xml -*** EXIT STATUS -*** ENVIRONMENT - Note that it does *not* currently use cryptsource or crypttarget. -*** FILES - Describe the key files and the key ring files. Also note that - they should normally have been automatically created. -*** BUGS -*** EXAMPLE - Examples of normal usage, debug usage, debugging by connecting - directly, etc. -*** SECURITY -*** SEE ALSO - Update from mandos.xml ** [#B] Temporarily lower kernel log level for less printouts during sucessfull boot. ** IPv4 support === modified file 'mandos-options.xml' --- mandos-options.xml 2008-08-30 18:45:41 +0000 +++ mandos-options.xml 2008-09-03 05:04:40 +0000 @@ -5,6 +5,8 @@
@@ -43,15 +45,14 @@ - GnuTLS priority string for the TLS handshake - with the clients. The default is - SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP. See - gnutls_priority_init + GnuTLS priority string for the TLS handshake. + The default is SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP. See + gnutls_priority_init 3 for the syntax. Warning: changing this may make the - TLS handshake fail, making communication with - clients impossible. + TLS handshake fail, making server-client + communication impossible. === modified file 'mandos.xml' --- mandos.xml 2008-09-01 08:29:23 +0000 +++ mandos.xml 2008-09-02 17:42:53 +0000 @@ -3,7 +3,7 @@ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ - + ]> @@ -115,24 +115,20 @@ Any authenticated client is then given the stored pre-encrypted password for that specific client. - PURPOSE - The purpose of this is to enable remote and unattended rebooting of client host computer with an encrypted root file system. See for details. - OPTIONS - === modified file 'plugin-runner.xml' --- plugin-runner.xml 2008-09-02 10:27:08 +0000 +++ plugin-runner.xml 2008-09-02 13:04:42 +0000 @@ -145,8 +145,8 @@ 5 for the root disk. The aim of this program is therefore to output a password, which then cryptsetup - 8 will use to try and - unlock the root disk. + 8 will use to unlock the + root disk. This program is not meant to be invoked directly, but can be in @@ -172,7 +172,7 @@ - - @@ -241,9 +241,9 @@ with the option argument baz is either --options-for=foo:--bar=baz or - --options-for=foo:--bar,baz, but - not - --options-for="foo:--bar baz". + --options-for=foo:--bar,baz. Using + --options-for="foo:--bar baz". will + not work. @@ -403,6 +403,39 @@ code will make this plugin-runner output the password from that plugin, stop any other plugins, and exit. + + + WRITING PLUGINS + + A plugin is simply a program which prints a password to its + standard output and then exits with a successful (zero) exit + status. If the exit status is not zero, any output on + standard output will be ignored by the plugin runner. Any + output on its standard error channel will simply be passed to + the standard error of the plugin runner, usually the system + console. + + + The plugin will run in the initial RAM disk environment, so + care must be taken not to depend on any files or running + services not available there. + + + The plugin must exit cleanly and free all allocated resources + upon getting the TERM signal, since this is what the plugin + runner uses to stop all other plugins when one plugin has + output a password and exited cleanly. + + + The plugin must not use resources, like for instance reading + from the standard input, without knowing that no other plugins + are also using it. + + + It is useful, but not required, for the plugin to take the + option. + + @@ -477,21 +510,98 @@ - - BUGS - - - + + + + + EXAMPLE - - + + + Normal invocation needs no options: + + + &COMMANDNAME; + + + + + Run the program, but not the plugins, in debug mode: + + + + + &COMMANDNAME; --debug + + + + + + Run all plugins, but run the foo plugin in + debug mode: + + + + + &COMMANDNAME; --options-for=foo:--debug + + + + + + Run all plugins, but not the program, in debug mode: + + + + + &COMMANDNAME; --global-options=--debug + + + + + + Run plugins from a different directory and add a special + option to the password-request + 8mandos plugin: + + + + +&COMMANDNAME; --plugin-dir=plugins.d --options-for=password-request:--keydir=keydir + + + - SECURITY + This program will, when starting, try to switch to another user. + If it is started as root, it will succeed, and will by default + switch to user and group 65534, which are assumed to be + non-privileged. This user and group is then what all plugins + will be started as. Therefore, the only way to run a plugin as + a privileged user is to have the set-user-ID or set-group-ID bit + set on the plugin executable files (see + execve2 + ). + + + If this program is used as a keyscript in crypttab5 + , there is a risk that if this program fails to + work, there might be no way to boot the system except for + booting from another media and editing the initial RAM disk + image to not run this program. This is, however, unlikely, + since the password-prompt8mandos + plugin will read a password from the console in + case of failure of the other plugins, and this plugin runner + will also, in case of catastrophic failure, itself fall back to + asking and outputting a password on the console (see ). @@ -500,6 +610,10 @@ cryptsetup 8, + crypttab + 5, + execve + 2, mandos 8, password-prompt === modified file 'plugins.d/password-request.c' --- plugins.d/password-request.c 2008-09-03 18:47:50 +0000 +++ plugins.d/password-request.c 2008-09-03 19:04:05 +0000 @@ -345,7 +345,7 @@ } plaintext_length += ret; } - + if(debug){ fprintf(stderr, "Decrypted password is: "); for(ssize_t i = 0; i < plaintext_length; i++){ @@ -412,8 +412,8 @@ } if(debug){ - fprintf(stderr, "Attempting to use OpenPGP certificate %s" - " and keyfile %s as GnuTLS credentials\n", pubkeyfilename, + fprintf(stderr, "Attempting to use OpenPGP public key %s and" + " secret key %s as GnuTLS credentials\n", pubkeyfilename, seckeyfilename); } @@ -444,15 +444,14 @@ } gnutls_certificate_set_dh_params(mc->cred, mc->dh_params); - + return 0; - + globalfail: - + gnutls_certificate_free_credentials(mc->cred); gnutls_global_deinit(); return -1; - } static int init_gnutls_session(mandos_context *mc, @@ -530,7 +529,7 @@ perror("socket"); return -1; } - + if(debug){ if(if_indextoname((unsigned int)if_index, interface) == NULL){ perror("if_indextoname"); @@ -575,7 +574,7 @@ perror("connect"); return -1; } - + const char *out = mandos_protocol_version; written = 0; while (true){ @@ -599,13 +598,13 @@ } } } - + if(debug){ fprintf(stderr, "Establishing TLS session with %s\n", ip); } gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd); - + do{ ret = gnutls_handshake (session); } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED); @@ -625,7 +624,7 @@ fprintf(stderr, "Retrieving pgp encrypted password from %s\n", ip); } - + while(true){ buffer_capacity = adjustbuffer(&buffer, buffer_length, buffer_capacity); @@ -828,7 +827,8 @@ const char *pubkey = PATHDIR "/" PUBKEY; mandos_context mc = { .simple_poll = NULL, .server = NULL, - .dh_bits = 1024, .priority = "SECURE256"}; + .dh_bits = 1024, .priority = "SECURE256" + ":!CTYPE-X.509:+CTYPE-OPENPGP" }; bool gnutls_initalized = false; bool pgpme_initalized = false; @@ -837,53 +837,55 @@ { .name = "debug", .key = 128, .doc = "Debug mode", .group = 3 }, { .name = "connect", .key = 'c', - .arg = "IP", - .doc = "Connect directly to a sepcified mandos server", + .arg = "ADDRESS:PORT", + .doc = "Connect directly to a specific Mandos server", .group = 1 }, { .name = "interface", .key = 'i', - .arg = "INTERFACE", - .doc = "Interface that Avahi will conntect through", + .arg = "NAME", + .doc = "Interface that will be used to search for Mandos" + " servers", .group = 1 }, { .name = "seckey", .key = 's', - .arg = "SECKEY", - .doc = "Secret openpgp key for gnutls authentication", + .arg = "FILE", + .doc = "OpenPGP secret key file base name", .group = 1 }, { .name = "pubkey", .key = 'p', - .arg = "PUBKEY", - .doc = "Public openpgp key for gnutls authentication", + .arg = "FILE", + .doc = "OpenPGP public key file base name", .group = 2 }, { .name = "dh-bits", .key = 129, .arg = "BITS", - .doc = "dh-bits to use in gnutls communication", + .doc = "Bit length of the prime number used in the" + " Diffie-Hellman key exchange", .group = 2 }, { .name = "priority", .key = 130, - .arg = "PRIORITY", - .doc = "GNUTLS priority", .group = 1 }, + .arg = "STRING", + .doc = "GnuTLS priority string for the TLS handshake", + .group = 1 }, { .name = NULL } }; - 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 plugin list pointer. */ switch (key) { - case 128: + case 128: /* --debug */ debug = true; break; - case 'c': + case 'c': /* --connect */ connect_to = arg; break; - case 'i': + case 'i': /* --interface */ interface = arg; break; - case 's': + case 's': /* --seckey */ seckey = arg; break; - case 'p': + case 'p': /* --pubkey */ pubkey = arg; break; - case 129: + case 129: /* --dh-bits */ errno = 0; mc.dh_bits = (unsigned int) strtol(arg, NULL, 10); if (errno){ @@ -891,7 +893,7 @@ exit(EXIT_FAILURE); } break; - case 130: + case 130: /* --priority */ mc.priority = arg; break; case ARGP_KEY_ARG: @@ -903,11 +905,11 @@ } return 0; } - + struct argp argp = { .options = options, .parser = parse_opt, .args_doc = "", .doc = "Mandos client -- Get and decrypt" - " passwords from mandos server" }; + " passwords from a Mandos server" }; ret = argp_parse (&argp, argc, argv, 0, 0, NULL); if (ret == ARGP_ERR_UNKNOWN){ fprintf(stderr, "Unknown error while parsing arguments\n"); @@ -915,7 +917,7 @@ goto end; } } - + ret = init_gnutls_global(&mc, pubkey, seckey); if (ret == -1){ fprintf(stderr, "init_gnutls_global failed\n"); @@ -1030,7 +1032,7 @@ exitcode = EXIT_FAILURE; goto end; } - + { AvahiServerConfig config; /* Do not publish any local Zeroconf records */ @@ -1039,12 +1041,12 @@ config.publish_addresses = 0; config.publish_workstation = 0; config.publish_domain = 0; - + /* Allocate a new server */ mc.server = avahi_server_new(avahi_simple_poll_get (mc.simple_poll), &config, NULL, NULL, &error); - + /* Free the Avahi configuration data */ avahi_server_config_free(&config); } @@ -1070,7 +1072,7 @@ } /* Run the main loop */ - + if (debug){ fprintf(stderr, "Starting Avahi loop search\n"); } @@ -1078,7 +1080,7 @@ avahi_simple_poll_loop(mc.simple_poll); end: - + if (debug){ fprintf(stderr, "%s exiting\n", argv[0]); } @@ -1089,10 +1091,10 @@ if (mc.server != NULL) avahi_server_free(mc.server); - + if (mc.simple_poll != NULL) avahi_simple_poll_free(mc.simple_poll); - + if (gnutls_initalized){ gnutls_certificate_free_credentials(mc.cred); gnutls_global_deinit (); === modified file 'plugins.d/password-request.xml' --- plugins.d/password-request.xml 2008-08-31 15:06:39 +0000 +++ plugins.d/password-request.xml 2008-09-03 17:34:29 +0000 @@ -3,7 +3,7 @@ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ - + ]> @@ -124,36 +124,72 @@ DESCRIPTION - &COMMANDNAME; is a mandos plugin that works - like a client program that through avahi detects mandos servers, - sets up a gnutls connect and request a encrypted password. Any - passwords given is automaticly decrypted and passed to - cryptsetup. + &COMMANDNAME; is a client program that + communicates with mandos8 + to get a password. It uses IPv6 link-local addresses to get + network connectivity, Zeroconf to find servers, and TLS with an + OpenPGP key to ensure authenticity and confidentiality. It + keeps running, trying all servers on the network, until it + receives a satisfactory reply or a TERM signal is recieved. + + + This program is not meant to be run directly; it is really meant + to run as a plugin of the Mandos + plugin-runner + 8mandos, which runs in the + initial RAM disk environment because it is + specified as a keyscript in the + crypttab5 + file. + + + + + PURPOSE + + The purpose of this is to enable remote and unattended + rebooting of client host computer with an + encrypted root file system. See for details. OPTIONS - Commonly not invoked as command lines but from configuration - file of plugin runner. + This program is commonly not invoked from the command line; it + is normally started by the Mandos + plugin runner, see plugin-runner8mandos + . Any command line options this program accepts + are therefore normally provided by the plugin runner, and not + directly. - + - Connect directly to a specified mandos server + Do not use Zeroconf to locate servers. Connect directly + to only one specified Mandos + server. Note that an IPv6 address has colon characters in + it, so the last colon character is + assumed to separate the address from the port number. + + + This option is normally only useful for testing and + debugging. - + @@ -161,7 +197,11 @@ DIRECTORY - Directory where the openpgp keyring is + Directory to read the OpenPGP key files + pubkey.txt and + seckey.txt from. The default is + /conf/conf.d/mandos (in the initial + RAM disk environment). @@ -173,11 +213,18 @@ NAME - Interface that Avahi will connect through + Network interface that will be brought up and scanned for + Mandos servers to connect to. The default it + eth0. + + + If the option is used, this + specifies the interface to use to connect to the address + given. - + @@ -185,7 +232,10 @@ FILE - Public openpgp key for gnutls authentication + OpenPGP public key file base name. This will be combined + with the directory from the + option to form an absolute file name. The default name is + pubkey.txt. @@ -197,7 +247,10 @@ FILE - Secret OpenPGP key for GnuTLS authentication + OpenPGP secret key file base name. This will be combined + with the directory from the + option to form an absolute file name. The default name is + seckey.txt. @@ -206,9 +259,8 @@ - - GnuTLS priority - + @@ -217,7 +269,8 @@ >BITS - DH bits to use in gnutls communication + Sets the number of bits to use for the prime number in the + TLS Diffie-Hellman key exchange. Default is 1024. @@ -226,7 +279,14 @@ - Debug mode + Enable debug mode. This will enable a lot of output to + standard error about what the program is doing. The + program will still perform all other functions normally. + + + It will also enable debug mode in the Avahi and GnuTLS + libraries, making them print large amounts of debugging + output. @@ -236,7 +296,7 @@ - Gives a help message + Gives a help message about options and their meanings. @@ -245,7 +305,7 @@ - Gives a short usage message + Gives a short usage message. @@ -255,52 +315,189 @@ - Prints the program version + Prints the program version. + + OVERVIEW + + + This program is the client part. It is a plugin started by + plugin-runner + 8mandos which will run in + an initial RAM disk environment. + + + This program could, theoretically, be used as a keyscript in + /etc/crypttab, but it would then be + impossible to enter a password for the encrypted root disk at + the console, since this program does not read from the console + at all. This is why a separate plugin ( + password-prompt + 8mandos) does that, which + will be run in parallell to this one by the plugin runner. + + + EXIT STATUS + This program will exit with a successful (zero) exit status if a + server could be found and the password received from it could be + successfully decrypted and output on standard output. The + program will exit with a non-zero exit status only if a critical + error occurs. Otherwise, it will forever connect to new + Mandos servers as they appear, trying + to get a decryptable password. - + ENVIRONMENT + This program does not use any environment variables, not even + the ones provided by cryptsetup8 + . - + FILES - - + + + /conf/conf.d/mandos/pubkey.txt + /conf/conf.d/mandos/seckey.txt + + + OpenPGP public and private key files, in ASCII + Armor format. These are the default file names, + they can be changed with the and + options. + + + + - - BUGS - - - + + + + + EXAMPLE + Note that normally, command line options will not be given + directly, but via options for the Mandos plugin-runner + 8mandos. + + + Normal invocation needs no options, if the network interface + is eth0: + + + &COMMANDNAME; + + + + + Search for Mandos servers on another interface: + + + + &COMMANDNAME; --interface eth1 + + + + + Run in debug mode, and use a custom key directory: + + + + &COMMANDNAME; --debug --keydir keydir + + + + + Run in debug mode, with a custom key directory, and do not use + Zeroconf to locate a server; connect directly to the IPv6 + address 2001:db8:f983:bd0b:30de:ae4a:71f2:f672, + port 4711, using interface eth2: + + + + +&COMMANDNAME; --debug --keydir keydir --connect 2001:db8:f983:bd0b:30de:ae4a:71f2:f672:4711 --interface eth2 + + + SECURITY + This program is set-uid to root, but will switch back to the + original (and presumably non-privileged) user and group after + bringing up the network interface. + + + To use this program for its intended purpose (see ), the password for the root file system will + have to be given out to be stored in a server computer, after + having been encrypted using an OpenPGP key. This encrypted data + which will be stored in a server can only be decrypted by the + OpenPGP key, and the data will only be given out to those + clients who can prove they actually have that key. This key, + however, is stored unencrypted on the client side in its initial + RAM disk image file system. This is normally + readable by all, but this is normally fixed during installation + of this program; file permissions are set so that no-one is able + to read that file. + + + The only remaining weak point is that someone with physical + access to the client hard drive might turn off the client + computer, read the OpenPGP keys directly from the hard drive, + and communicate with the server. The defense against this is + that the server is supposed to notice the client disappearing + and will stop giving out the encrypted data. Therefore, it is + important to set the timeout and checker interval values tightly + on the server. See mandos8. + + + It will also help if the checker program on the server is + configured to request something from the client which can not be + spoofed by someone else on the network, unlike unencrypted + ICMP echo (ping) replies. + + + Note: This makes it completely insecure to + have Mandos clients which dual-boot + to another operating system which is not + trusted to keep the initial RAM disk image + confidential. SEE ALSO + cryptsetup + 8, + crypttab + 5, mandos 8, password-prompt @@ -308,42 +505,121 @@ plugin-runner 8mandos - - - Zeroconf - - - - Avahi - - - - GnuTLS - - - - - GPGME - - - - RFC 4880: OpenPGP Message - Format - - - - RFC 5081: Using OpenPGP Keys for - Transport Layer Security - - - - RFC 4291: IP Version 6 Addressing - Architecture, section 2.5.6, Link-Local IPv6 - Unicast Addresses - - + + + + Zeroconf + + + + Zeroconf is the network protocol standard used for finding + Mandos servers on the local network. + + + + + + Avahi + + + + Avahi is the library this program calls to find Zeroconf + services. + + + + + + GnuTLS + + + + GnuTLS is the library this client uses to implement TLS for + communicating securely with the server, and at the same time + send the public OpenPGP key to the server. + + + + + + GPGME + + + + GPGME is the library used to decrypt the OpenPGP data sent + by the server. + + + + + + RFC 4291: IP Version 6 Addressing + Architecture + + + + + Section 2.2: Text Representation of + Addresses + + + + Section 2.5.5.2: IPv4-Mapped IPv6 + Address + + + + Section 2.5.6, Link-Local IPv6 Unicast + Addresses + + + This client uses IPv6 link-local addresses, which are + immediately usable since a link-local addresses is + automatically assigned to a network interfaces when it + is brought up. + + + + + + + + + RFC 4346: The Transport Layer Security (TLS) + Protocol Version 1.1 + + + + TLS 1.1 is the protocol implemented by GnuTLS. + + + + + + RFC 4880: OpenPGP Message Format + + + + The data received from the server is binary encrypted + OpenPGP data. + + + + + + RFC 5081: Using OpenPGP Keys for Transport Layer + Security + + + + This is implemented by GnuTLS and used by this program so + that OpenPGP keys can be used. + + + +