=== modified file 'INSTALL' --- INSTALL 2008-10-28 18:00:20 +0000 +++ INSTALL 2009-01-27 00:07:26 +0000 @@ -43,13 +43,14 @@ + Python-GnuTLS 1.1.5 http://pypi.python.org/pypi/python-gnutls/ + dbus-python 0.82.4 http://dbus.freedesktop.org/doc/dbus-python/ + python-ctypes 1.0.0 http://pypi.python.org/pypi/ctypes + + PyGObject 2.14.2 http://library.gnome.org/devel/pygobject/ Strongly recommended: + fping 2.4b2-to-ipv6 http://www.fping.com/ Package names: python-gnutls avahi-daemon python python-avahi python-dbus - python-ctypes + python-ctypes python-gobject *** Mandos Client + initramfs-tools 0.85i === modified file 'Makefile' --- Makefile 2009-01-17 02:12:05 +0000 +++ Makefile 2009-02-03 22:41:49 +0000 @@ -213,7 +213,7 @@ # Run the server with a local config run-server: confdir/mandos.conf confdir/clients.conf - ./mandos --debug --configdir=confdir $(SERVERARGS) + ./mandos --debug --no-dbus --configdir=confdir $(SERVERARGS) # Used by run-server confdir/mandos.conf: mandos.conf === modified file 'TODO' --- TODO 2009-01-19 05:59:35 +0000 +++ TODO 2009-01-31 10:33:17 +0000 @@ -1,6 +1,7 @@ -*- org -*- * mandos-client +** TODO [#A] Clean up /tmp directory on signal ** TODO [#C] IPv4 support * plugin-runner === modified file 'debian/control' --- debian/control 2009-01-06 20:39:35 +0000 +++ debian/control 2009-01-26 23:47:44 +0000 @@ -15,7 +15,8 @@ Package: mandos Architecture: all Depends: ${misc:Depends}, python (>=2.5), python-gnutls, python-dbus, - python-avahi, avahi-daemon, gnupg (< 2), adduser + python-avahi, python-gobject, avahi-daemon, gnupg (< 2), + adduser Recommends: fping Description: a server giving encrypted passwords to Mandos clients This is the server part of the Mandos system, which allows === modified file 'initramfs-tools-script' --- initramfs-tools-script 2009-01-15 02:52:02 +0000 +++ initramfs-tools-script 2009-02-03 22:41:49 +0000 @@ -14,14 +14,14 @@ PREREQ="" prereqs() { - echo "$PREREQ" + echo "$PREREQ" } case $1 in prereqs) - prereqs - exit 0 - ;; + prereqs + exit 0 + ;; esac for param in `cat /proc/cmdline`; do @@ -32,7 +32,8 @@ chmod a=rwxt /tmp -test -w /conf/conf.d/cryptroot +test -r /conf/conf.d/cryptroot +test -w /conf/conf.d # Do not replace cryptroot file unless we need to. replace_cryptroot=no === modified file 'mandos' --- mandos 2009-01-20 02:23:59 +0000 +++ mandos 2009-02-03 22:41:49 +0000 @@ -73,12 +73,13 @@ (facility = logging.handlers.SysLogHandler.LOG_DAEMON, address = "/dev/log")) syslogger.setFormatter(logging.Formatter - ('Mandos: %(levelname)s: %(message)s')) + ('Mandos [%(process)d]: %(levelname)s:' + ' %(message)s')) logger.addHandler(syslogger) console = logging.StreamHandler() -console.setFormatter(logging.Formatter('%(name)s: %(levelname)s:' - ' %(message)s')) +console.setFormatter(logging.Formatter('%(name)s [%(process)d]:' + ' %(levelname)s: %(message)s')) logger.addHandler(console) class AvahiError(Exception): @@ -329,7 +330,7 @@ if exitstatus == 0: logger.info(u"Checker for %(name)s succeeded", vars(self)) - self.bump_timeout() + self.checked_ok() else: logger.info(u"Checker for %(name)s failed", vars(self)) @@ -347,7 +348,7 @@ dbus.Int64(condition), dbus.String(command)) - def bump_timeout(self): + def checked_ok(self): """Bump up the timeout for this client. This should only be called when the client has been seen, alive and well. @@ -451,9 +452,9 @@ ## D-Bus methods & signals _interface = u"se.bsnet.fukt.Mandos.Client" - # BumpTimeout - method - BumpTimeout = dbus.service.method(_interface)(bump_timeout) - BumpTimeout.__name__ = "BumpTimeout" + # CheckedOK - method + CheckedOK = dbus.service.method(_interface)(checked_ok) + CheckedOK.__name__ = "CheckedOK" # CheckerCompleted - signal @dbus.service.signal(_interface, signature="nxs") @@ -595,10 +596,13 @@ != gnutls.library.constants.GNUTLS_CRT_OPENPGP): # ...do the normal thing return session.peer_certificate - list_size = ctypes.c_uint() + list_size = ctypes.c_uint(1) cert_list = (gnutls.library.functions .gnutls_certificate_get_peers (session._c_object, ctypes.byref(list_size))) + if not bool(cert_list) and list_size.value != 0: + raise gnutls.errors.GNUTLSError("error getting peer" + " certificate") if list_size.value == 0: return None cert = cert_list[0] @@ -673,8 +677,8 @@ # using OpenPGP certificates. #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC", - # "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP", - # "+DHE-DSS")) + # "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP", + # "+DHE-DSS")) # Use a fallback default, since this MUST be set. priority = self.server.settings.get("priority", "NORMAL") (gnutls.library.functions @@ -688,6 +692,7 @@ # Do not run session.bye() here: the session is not # established. Just abandon the request. return + logger.debug(u"Handshake succeeded") try: fpr = fingerprint(peer_certificate(session)) except (TypeError, gnutls.errors.GNUTLSError), error: @@ -695,6 +700,7 @@ session.bye() return logger.debug(u"Fingerprint: %s", fpr) + for c in self.server.clients: if c.fingerprint == fpr: client = c @@ -713,7 +719,7 @@ session.bye() return ## This won't work here, since we're in a fork. - # client.bump_timeout() + # client.checked_ok() sent_size = 0 while sent_size < len(client.secret): sent = session.send(client.secret[sent_size:]) @@ -759,7 +765,7 @@ u" bind to interface %s", self.settings["interface"]) else: - raise error + raise # Only bind(2) the socket if we really need to. if self.server_address[0] or self.server_address[1]: if not self.server_address[0]: @@ -786,7 +792,7 @@ def string_to_delta(interval): """Parse a string and return a datetime.timedelta - + >>> string_to_delta('7d') datetime.timedelta(7) >>> string_to_delta('60s') @@ -941,11 +947,14 @@ server_config.read(os.path.join(options.configdir, "mandos.conf")) # Convert the SafeConfigParser object to a dict server_settings = server_config.defaults() - # Use getboolean on the boolean config options - server_settings["debug"] = (server_config.getboolean - ("DEFAULT", "debug")) - server_settings["use_dbus"] = (server_config.getboolean - ("DEFAULT", "use_dbus")) + # Use the appropriate methods on the non-string config options + server_settings["debug"] = server_config.getboolean("DEFAULT", + "debug") + server_settings["use_dbus"] = server_config.getboolean("DEFAULT", + "use_dbus") + if server_settings["port"]: + server_settings["port"] = server_config.getint("DEFAULT", + "port") del server_config # Override the settings from the config file with command line @@ -992,7 +1001,7 @@ pidfilename = "/var/run/mandos.pid" try: pidfile = open(pidfilename, "w") - except IOError, error: + except IOError: logger.error("Could not open file %r", pidfilename) try: @@ -1010,12 +1019,25 @@ uid = 65534 gid = 65534 try: + os.setgid(gid) os.setuid(uid) - os.setgid(gid) except OSError, error: if error[0] != errno.EPERM: raise error + # Enable all possible GnuTLS debugging + if debug: + # "Use a log level over 10 to enable all debugging options." + # - GnuTLS manual + gnutls.library.functions.gnutls_global_set_log_level(11) + + @gnutls.library.types.gnutls_log_func + def debug_gnutls(level, string): + logger.debug("GnuTLS: %s", string[:-1]) + + (gnutls.library.functions + .gnutls_global_set_log_function(debug_gnutls)) + global service service = AvahiService(name = server_settings["servicename"], servicetype = "_mandos._tcp", ) @@ -1110,10 +1132,12 @@ @dbus.service.method(_interface, out_signature="ao") def GetAllClients(self): + "D-Bus method" return dbus.Array(c.dbus_object_path for c in clients) @dbus.service.method(_interface, out_signature="a{oa{sv}}") def GetAllClientsWithProperties(self): + "D-Bus method" return dbus.Dictionary( ((c.dbus_object_path, c.GetAllProperties()) for c in clients), @@ -1121,6 +1145,7 @@ @dbus.service.method(_interface, in_signature="o") def RemoveClient(self, object_path): + "D-Bus method" for c in clients: if c.dbus_object_path == object_path: clients.remove(c) @@ -1175,7 +1200,9 @@ sys.exit(1) except KeyboardInterrupt: if debug: - print + print >> sys.stderr + logger.debug("Server received KeyboardInterrupt") + logger.debug("Server exiting") if __name__ == '__main__': main() === modified file 'mandos-ctl' --- mandos-ctl 2009-02-06 01:13:58 +0000 +++ mandos-ctl 2009-02-06 01:15:52 +0000 @@ -140,7 +140,7 @@ help="Set password blob (file) for client") options, client_names = parser.parse_args() -# Compile list of clients to processed +# Compile list of clients to process clients=[] for name in client_names: for path, client in mandos_clients.iteritems(): === modified file 'plugin-runner.c' --- plugin-runner.c 2009-01-17 02:34:57 +0000 +++ plugin-runner.c 2009-02-05 03:37:06 +0000 @@ -89,8 +89,8 @@ size_t buffer_size; size_t buffer_length; bool eof; - volatile bool completed; - volatile int status; + volatile sig_atomic_t completed; + int status; struct plugin *next; } plugin; @@ -120,10 +120,10 @@ } } - *new_plugin = (plugin) { .name = copy_name, - .argc = 1, - .disabled = false, - .next = plugin_list }; + *new_plugin = (plugin){ .name = copy_name, + .argc = 1, + .disabled = false, + .next = plugin_list }; new_plugin->argv = malloc(sizeof(char *) * 2); if(new_plugin->argv == NULL){ @@ -223,6 +223,7 @@ /* Mark processes as completed when they exit, and save their exit status. */ static void handle_sigchld(__attribute__((unused)) int sig){ + int old_errno = errno; while(true){ plugin *proc = plugin_list; int status; @@ -232,11 +233,11 @@ break; } if(pid == -1){ - if(errno != ECHILD){ - perror("waitpid"); + if(errno == ECHILD){ + /* No child processes */ + break; } - /* No child processes */ - break; + perror("waitpid"); } /* A child exited, find it in process_list */ @@ -248,8 +249,9 @@ continue; } proc->status = status; - proc->completed = true; + proc->completed = 1; } + errno = old_errno; } /* Prints out a password to stdout */ @@ -376,8 +378,8 @@ }; error_t parse_opt(int key, char *arg, __attribute__((unused)) - struct argp_state *state) { - switch(key) { + struct argp_state *state){ + switch(key){ case 'g': /* --global-options */ if(arg != NULL){ char *p; @@ -512,8 +514,8 @@ ignores everything but the --config-file option. */ error_t parse_opt_config_file(int key, char *arg, __attribute__((unused)) - struct argp_state *state) { - switch(key) { + struct argp_state *state){ + switch(key){ case 'g': /* --global-options */ case 'G': /* --global-env */ case 'o': /* --options-for */ @@ -662,13 +664,13 @@ } /* Strip permissions down to nobody */ + setgid(gid); + if(ret == -1){ + perror("setgid"); + } ret = setuid(uid); if(ret == -1){ perror("setuid"); - } - setgid(gid); - if(ret == -1){ - perror("setgid"); } if(plugindir == NULL){ @@ -959,7 +961,7 @@ from one of them */ for(plugin *proc = plugin_list; proc != NULL;){ /* Is this process completely done? */ - if(proc->eof and proc->completed){ + if(proc->completed and proc->eof){ /* Only accept the plugin output if it exited cleanly */ if(not WIFEXITED(proc->status) or WEXITSTATUS(proc->status) != 0){ @@ -970,7 +972,7 @@ fprintf(stderr, "Plugin %" PRIdMAX " exited with status" " %d\n", (intmax_t) (proc->pid), WEXITSTATUS(proc->status)); - } else if(WIFSIGNALED(proc->status)) { + } else if(WIFSIGNALED(proc->status)){ fprintf(stderr, "Plugin %" PRIdMAX " killed by signal" " %d\n", (intmax_t) (proc->pid), WTERMSIG(proc->status)); === modified file 'plugins.d/mandos-client.c' --- plugins.d/mandos-client.c 2009-01-17 12:12:26 +0000 +++ plugins.d/mandos-client.c 2009-02-04 06:52:40 +0000 @@ -36,7 +36,8 @@ #define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */ #include /* fprintf(), stderr, fwrite(), - stdout, ferror(), sscanf */ + stdout, ferror(), sscanf(), + remove() */ #include /* uint16_t, uint32_t */ #include /* NULL, size_t, ssize_t */ #include /* free(), EXIT_SUCCESS, EXIT_FAILURE, @@ -60,7 +61,7 @@ #include /* PRIu16, intmax_t, SCNdMAX */ #include /* assert() */ #include /* perror(), errno */ -#include /* time(), nanosleep() */ +#include /* nanosleep(), time() */ #include /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS, if_indextoname(), if_nametoindex(), IF_NAMESIZE */ @@ -74,7 +75,9 @@ argp_state, struct argp, argp_parse(), ARGP_KEY_ARG, ARGP_KEY_END, ARGP_ERR_UNKNOWN */ +#ifdef __linux__ #include /* klogctl() */ +#endif /* Avahi */ /* All Avahi types, constants and functions @@ -154,7 +157,7 @@ /* - * Helper function to insert pub and seckey to the enigne keyring. + * Helper function to insert pub and seckey to the engine keyring. */ bool import_key(const char *filename){ int fd; @@ -365,7 +368,7 @@ return plaintext_length; } -static const char * safer_gnutls_strerror(int value) { +static const char * safer_gnutls_strerror(int value){ const char *ret = gnutls_strerror(value); /* Spurious warning from -Wunreachable-code */ if(ret == NULL) @@ -389,7 +392,7 @@ } ret = gnutls_global_init(); - if(ret != GNUTLS_E_SUCCESS) { + if(ret != GNUTLS_E_SUCCESS){ fprintf(stderr, "GnuTLS global_init: %s\n", safer_gnutls_strerror(ret)); return -1; @@ -424,7 +427,7 @@ ret = gnutls_certificate_set_openpgp_key_file (mc->cred, pubkeyfilename, seckeyfilename, GNUTLS_OPENPGP_FMT_BASE64); - if(ret != GNUTLS_E_SUCCESS) { + if(ret != GNUTLS_E_SUCCESS){ fprintf(stderr, "Error[%d] while reading the OpenPGP key pair ('%s'," " '%s')\n", ret, pubkeyfilename, seckeyfilename); @@ -435,13 +438,13 @@ /* GnuTLS server initialization */ ret = gnutls_dh_params_init(&mc->dh_params); - if(ret != GNUTLS_E_SUCCESS) { + if(ret != GNUTLS_E_SUCCESS){ fprintf(stderr, "Error in GnuTLS DH parameter initialization:" " %s\n", safer_gnutls_strerror(ret)); goto globalfail; } ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits); - if(ret != GNUTLS_E_SUCCESS) { + if(ret != GNUTLS_E_SUCCESS){ fprintf(stderr, "Error in GnuTLS prime generation: %s\n", safer_gnutls_strerror(ret)); goto globalfail; @@ -472,7 +475,7 @@ { const char *err; ret = gnutls_priority_set_direct(*session, mc->priority, &err); - if(ret != GNUTLS_E_SUCCESS) { + if(ret != GNUTLS_E_SUCCESS){ fprintf(stderr, "Syntax error at: %s\n", err); fprintf(stderr, "GnuTLS error: %s\n", safer_gnutls_strerror(ret)); @@ -483,7 +486,7 @@ ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE, mc->cred); - if(ret != GNUTLS_E_SUCCESS) { + if(ret != GNUTLS_E_SUCCESS){ fprintf(stderr, "Error setting GnuTLS credentials: %s\n", safer_gnutls_strerror(ret)); gnutls_deinit(*session); @@ -531,7 +534,7 @@ } tcp_sd = socket(PF_INET6, SOCK_STREAM, 0); - if(tcp_sd < 0) { + if(tcp_sd < 0){ perror("socket"); return -1; } @@ -734,14 +737,14 @@ AVAHI_GCC_UNUSED AvahiStringList *txt, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, - void* userdata) { + void* userdata){ mandos_context *mc = userdata; assert(r); /* Called whenever a service has been resolved successfully or timed out */ - switch(event) { + switch(event){ default: case AVAHI_RESOLVER_FAILURE: fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'" @@ -767,23 +770,23 @@ avahi_s_service_resolver_free(r); } -static void browse_callback( AvahiSServiceBrowser *b, - AvahiIfIndex interface, - AvahiProtocol protocol, - AvahiBrowserEvent event, - const char *name, - const char *type, - const char *domain, - AVAHI_GCC_UNUSED AvahiLookupResultFlags - flags, - void* userdata) { +static void browse_callback(AvahiSServiceBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AVAHI_GCC_UNUSED AvahiLookupResultFlags + flags, + void* userdata){ mandos_context *mc = userdata; assert(b); /* Called whenever a new services becomes available on the LAN or is removed from the LAN */ - switch(event) { + switch(event){ default: case AVAHI_BROWSER_FAILURE: @@ -819,388 +822,406 @@ } int main(int argc, char *argv[]){ - AvahiSServiceBrowser *sb = NULL; - int error; - int ret; - intmax_t tmpmax; - int numchars; - int exitcode = EXIT_SUCCESS; - const char *interface = "eth0"; - struct ifreq network; - int sd; - uid_t uid; - gid_t gid; - char *connect_to = NULL; - char tempdir[] = "/tmp/mandosXXXXXX"; - AvahiIfIndex if_index = AVAHI_IF_UNSPEC; - const char *seckey = PATHDIR "/" SECKEY; - const char *pubkey = PATHDIR "/" PUBKEY; - - mandos_context mc = { .simple_poll = NULL, .server = NULL, - .dh_bits = 1024, .priority = "SECURE256" - ":!CTYPE-X.509:+CTYPE-OPENPGP" }; - bool gnutls_initalized = false; - bool gpgme_initalized = false; - double delay = 2.5; - - { - struct argp_option options[] = { - { .name = "debug", .key = 128, - .doc = "Debug mode", .group = 3 }, - { .name = "connect", .key = 'c', - .arg = "ADDRESS:PORT", - .doc = "Connect directly to a specific Mandos server", - .group = 1 }, - { .name = "interface", .key = 'i', - .arg = "NAME", - .doc = "Interface that will be used to search for Mandos" - " servers", - .group = 1 }, - { .name = "seckey", .key = 's', - .arg = "FILE", - .doc = "OpenPGP secret key file base name", - .group = 1 }, - { .name = "pubkey", .key = 'p', - .arg = "FILE", - .doc = "OpenPGP public key file base name", - .group = 2 }, - { .name = "dh-bits", .key = 129, - .arg = "BITS", - .doc = "Bit length of the prime number used in the" - " Diffie-Hellman key exchange", - .group = 2 }, - { .name = "priority", .key = 130, - .arg = "STRING", - .doc = "GnuTLS priority string for the TLS handshake", - .group = 1 }, - { .name = "delay", .key = 131, - .arg = "SECONDS", - .doc = "Maximum delay to wait for interface startup", - .group = 2 }, - { .name = NULL } - }; - - error_t parse_opt(int key, char *arg, - struct argp_state *state) { - switch(key) { - case 128: /* --debug */ - debug = true; - break; - case 'c': /* --connect */ - connect_to = arg; - break; - case 'i': /* --interface */ - interface = arg; - break; - case 's': /* --seckey */ - seckey = arg; - break; - case 'p': /* --pubkey */ - pubkey = arg; - break; - case 129: /* --dh-bits */ - ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars); - if(ret < 1 or tmpmax != (typeof(mc.dh_bits))tmpmax - or arg[numchars] != '\0'){ - fprintf(stderr, "Bad number of DH bits\n"); - exit(EXIT_FAILURE); - } - mc.dh_bits = (typeof(mc.dh_bits))tmpmax; - break; - case 130: /* --priority */ - mc.priority = arg; - break; - case 131: /* --delay */ - ret = sscanf(arg, "%lf%n", &delay, &numchars); - if(ret < 1 or arg[numchars] != '\0'){ - fprintf(stderr, "Bad delay\n"); - exit(EXIT_FAILURE); - } - break; - case ARGP_KEY_ARG: - argp_usage(state); - case ARGP_KEY_END: - break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; - } - - struct argp argp = { .options = options, .parser = parse_opt, - .args_doc = "", - .doc = "Mandos client -- Get and decrypt" - " 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"); - exitcode = EXIT_FAILURE; - goto end; - } - } - - /* If the interface is down, bring it up */ - { - // Lower kernel loglevel to KERN_NOTICE to avoid - // KERN_INFO messages to mess up the prompt - ret = klogctl(8, NULL, 5); - if(ret == -1){ - perror("klogctl"); - } - - sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); - if(sd < 0) { - perror("socket"); - exitcode = EXIT_FAILURE; + AvahiSServiceBrowser *sb = NULL; + int error; + int ret; + intmax_t tmpmax; + int numchars; + int exitcode = EXIT_SUCCESS; + const char *interface = "eth0"; + struct ifreq network; + int sd; + uid_t uid; + gid_t gid; + char *connect_to = NULL; + char tempdir[] = "/tmp/mandosXXXXXX"; + bool tempdir_created = false; + AvahiIfIndex if_index = AVAHI_IF_UNSPEC; + const char *seckey = PATHDIR "/" SECKEY; + const char *pubkey = PATHDIR "/" PUBKEY; + + mandos_context mc = { .simple_poll = NULL, .server = NULL, + .dh_bits = 1024, .priority = "SECURE256" + ":!CTYPE-X.509:+CTYPE-OPENPGP" }; + bool gnutls_initialized = false; + bool gpgme_initialized = false; + double delay = 2.5; + + { + struct argp_option options[] = { + { .name = "debug", .key = 128, + .doc = "Debug mode", .group = 3 }, + { .name = "connect", .key = 'c', + .arg = "ADDRESS:PORT", + .doc = "Connect directly to a specific Mandos server", + .group = 1 }, + { .name = "interface", .key = 'i', + .arg = "NAME", + .doc = "Interface that will be used to search for Mandos" + " servers", + .group = 1 }, + { .name = "seckey", .key = 's', + .arg = "FILE", + .doc = "OpenPGP secret key file base name", + .group = 1 }, + { .name = "pubkey", .key = 'p', + .arg = "FILE", + .doc = "OpenPGP public key file base name", + .group = 2 }, + { .name = "dh-bits", .key = 129, + .arg = "BITS", + .doc = "Bit length of the prime number used in the" + " Diffie-Hellman key exchange", + .group = 2 }, + { .name = "priority", .key = 130, + .arg = "STRING", + .doc = "GnuTLS priority string for the TLS handshake", + .group = 1 }, + { .name = "delay", .key = 131, + .arg = "SECONDS", + .doc = "Maximum delay to wait for interface startup", + .group = 2 }, + { .name = NULL } + }; + + error_t parse_opt(int key, char *arg, + struct argp_state *state){ + switch(key){ + case 128: /* --debug */ + debug = true; + break; + case 'c': /* --connect */ + connect_to = arg; + break; + case 'i': /* --interface */ + interface = arg; + break; + case 's': /* --seckey */ + seckey = arg; + break; + case 'p': /* --pubkey */ + pubkey = arg; + break; + case 129: /* --dh-bits */ + ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars); + if(ret < 1 or tmpmax != (typeof(mc.dh_bits))tmpmax + or arg[numchars] != '\0'){ + fprintf(stderr, "Bad number of DH bits\n"); + exit(EXIT_FAILURE); + } + mc.dh_bits = (typeof(mc.dh_bits))tmpmax; + break; + case 130: /* --priority */ + mc.priority = arg; + break; + case 131: /* --delay */ + ret = sscanf(arg, "%lf%n", &delay, &numchars); + if(ret < 1 or arg[numchars] != '\0'){ + fprintf(stderr, "Bad delay\n"); + exit(EXIT_FAILURE); + } + break; + case ARGP_KEY_ARG: + argp_usage(state); + case ARGP_KEY_END: + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; + } + + struct argp argp = { .options = options, .parser = parse_opt, + .args_doc = "", + .doc = "Mandos client -- Get and decrypt" + " 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"); + exitcode = EXIT_FAILURE; + goto end; + } + } + + /* If the interface is down, bring it up */ + { +#ifdef __linux__ + /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO + messages to mess up the prompt */ + ret = klogctl(8, NULL, 5); + if(ret == -1){ + perror("klogctl"); + } +#endif + + sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); + if(sd < 0){ + perror("socket"); + exitcode = EXIT_FAILURE; +#ifdef __linux__ + ret = klogctl(7, NULL, 0); + if(ret == -1){ + perror("klogctl"); + } +#endif + goto end; + } + strcpy(network.ifr_name, interface); + ret = ioctl(sd, SIOCGIFFLAGS, &network); + if(ret == -1){ + perror("ioctl SIOCGIFFLAGS"); +#ifdef __linux__ + ret = klogctl(7, NULL, 0); + if(ret == -1){ + perror("klogctl"); + } +#endif + exitcode = EXIT_FAILURE; + goto end; + } + if((network.ifr_flags & IFF_UP) == 0){ + network.ifr_flags |= IFF_UP; + ret = ioctl(sd, SIOCSIFFLAGS, &network); + if(ret == -1){ + perror("ioctl SIOCSIFFLAGS"); + exitcode = EXIT_FAILURE; +#ifdef __linux__ ret = klogctl(7, NULL, 0); if(ret == -1){ perror("klogctl"); } +#endif goto end; } - strcpy(network.ifr_name, interface); + } + /* sleep checking until interface is running */ + for(int i=0; i < delay * 4; i++){ ret = ioctl(sd, SIOCGIFFLAGS, &network); if(ret == -1){ perror("ioctl SIOCGIFFLAGS"); - ret = klogctl(7, NULL, 0); - if(ret == -1){ - perror("klogctl"); - } - exitcode = EXIT_FAILURE; - goto end; - } - if((network.ifr_flags & IFF_UP) == 0){ - network.ifr_flags |= IFF_UP; - ret = ioctl(sd, SIOCSIFFLAGS, &network); - if(ret == -1){ - perror("ioctl SIOCSIFFLAGS"); - exitcode = EXIT_FAILURE; - ret = klogctl(7, NULL, 0); - if(ret == -1){ - perror("klogctl"); - } - goto end; - } - } - // sleep checking until interface is running - for(int i=0; i < delay * 4; i++){ - ret = ioctl(sd, SIOCGIFFLAGS, &network); - if(ret == -1){ - perror("ioctl SIOCGIFFLAGS"); - } else if(network.ifr_flags & IFF_RUNNING){ - break; - } - struct timespec sleeptime = { .tv_nsec = 250000000 }; - nanosleep(&sleeptime, NULL); - } - ret = (int)TEMP_FAILURE_RETRY(close(sd)); - if(ret == -1){ - perror("close"); - } - // Restores kernel loglevel to default - ret = klogctl(7, NULL, 0); - if(ret == -1){ - perror("klogctl"); - } - } - - uid = getuid(); - gid = getgid(); - - ret = setuid(uid); - if(ret == -1){ - perror("setuid"); - } - - setgid(gid); - if(ret == -1){ - perror("setgid"); - } - - ret = init_gnutls_global(&mc, pubkey, seckey); - if(ret == -1){ - fprintf(stderr, "init_gnutls_global failed\n"); - exitcode = EXIT_FAILURE; - goto end; - } else { - gnutls_initalized = true; - } - - if(mkdtemp(tempdir) == NULL){ - perror("mkdtemp"); - tempdir[0] = '\0'; - goto end; - } - - if(not init_gpgme(&mc, pubkey, seckey, tempdir)){ - fprintf(stderr, "gpgme_initalized failed\n"); - exitcode = EXIT_FAILURE; - goto end; - } else { - gpgme_initalized = true; - } - - if_index = (AvahiIfIndex) if_nametoindex(interface); - if(if_index == 0){ - fprintf(stderr, "No such interface: \"%s\"\n", interface); - exit(EXIT_FAILURE); - } - - if(connect_to != NULL){ - /* Connect directly, do not use Zeroconf */ - /* (Mainly meant for debugging) */ - char *address = strrchr(connect_to, ':'); - if(address == NULL){ - fprintf(stderr, "No colon in address\n"); - exitcode = EXIT_FAILURE; - goto end; - } - uint16_t port; - ret = sscanf(address+1, "%" SCNdMAX "%n", &tmpmax, &numchars); - if(ret < 1 or tmpmax != (uint16_t)tmpmax - or address[numchars+1] != '\0'){ - fprintf(stderr, "Bad port number\n"); - exitcode = EXIT_FAILURE; - goto end; - } - port = (uint16_t)tmpmax; - *address = '\0'; - address = connect_to; - ret = start_mandos_communication(address, port, if_index, &mc); - if(ret < 0){ - exitcode = EXIT_FAILURE; - } else { - exitcode = EXIT_SUCCESS; - } - goto end; - } - - if(not debug){ - avahi_set_log_function(empty_log); - } - - /* Initialize the pseudo-RNG for Avahi */ - srand((unsigned int) time(NULL)); - - /* Allocate main Avahi loop object */ - mc.simple_poll = avahi_simple_poll_new(); - if(mc.simple_poll == NULL) { - fprintf(stderr, "Avahi: Failed to create simple poll" - " object.\n"); - exitcode = EXIT_FAILURE; - goto end; - } - - { - AvahiServerConfig config; - /* Do not publish any local Zeroconf records */ - avahi_server_config_init(&config); - config.publish_hinfo = 0; - 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); - } - - /* Check if creating the Avahi server object succeeded */ - if(mc.server == NULL) { - fprintf(stderr, "Failed to create Avahi server: %s\n", - avahi_strerror(error)); - exitcode = EXIT_FAILURE; - goto end; - } - - /* Create the Avahi service browser */ - sb = avahi_s_service_browser_new(mc.server, if_index, - AVAHI_PROTO_INET6, - "_mandos._tcp", NULL, 0, - browse_callback, &mc); - if(sb == NULL) { - fprintf(stderr, "Failed to create service browser: %s\n", - avahi_strerror(avahi_server_errno(mc.server))); - exitcode = EXIT_FAILURE; - goto end; - } - - /* Run the main loop */ - - if(debug){ - fprintf(stderr, "Starting Avahi loop search\n"); - } - - avahi_simple_poll_loop(mc.simple_poll); - + } else if(network.ifr_flags & IFF_RUNNING){ + break; + } + struct timespec sleeptime = { .tv_nsec = 250000000 }; + ret = nanosleep(&sleeptime, NULL); + if(ret == -1 and errno != EINTR){ + perror("nanosleep"); + } + } + ret = (int)TEMP_FAILURE_RETRY(close(sd)); + if(ret == -1){ + perror("close"); + } +#ifdef __linux__ + /* Restores kernel loglevel to default */ + ret = klogctl(7, NULL, 0); + if(ret == -1){ + perror("klogctl"); + } +#endif + } + + uid = getuid(); + gid = getgid(); + + setgid(gid); + if(ret == -1){ + perror("setgid"); + } + + ret = setuid(uid); + if(ret == -1){ + perror("setuid"); + } + + ret = init_gnutls_global(&mc, pubkey, seckey); + if(ret == -1){ + fprintf(stderr, "init_gnutls_global failed\n"); + exitcode = EXIT_FAILURE; + goto end; + } else { + gnutls_initialized = true; + } + + if(mkdtemp(tempdir) == NULL){ + perror("mkdtemp"); + goto end; + } + tempdir_created = true; + + if(not init_gpgme(&mc, pubkey, seckey, tempdir)){ + fprintf(stderr, "init_gpgme failed\n"); + exitcode = EXIT_FAILURE; + goto end; + } else { + gpgme_initialized = true; + } + + if_index = (AvahiIfIndex) if_nametoindex(interface); + if(if_index == 0){ + fprintf(stderr, "No such interface: \"%s\"\n", interface); + exitcode = EXIT_FAILURE; + goto end; + } + + if(connect_to != NULL){ + /* Connect directly, do not use Zeroconf */ + /* (Mainly meant for debugging) */ + char *address = strrchr(connect_to, ':'); + if(address == NULL){ + fprintf(stderr, "No colon in address\n"); + exitcode = EXIT_FAILURE; + goto end; + } + uint16_t port; + ret = sscanf(address+1, "%" SCNdMAX "%n", &tmpmax, &numchars); + if(ret < 1 or tmpmax != (uint16_t)tmpmax + or address[numchars+1] != '\0'){ + fprintf(stderr, "Bad port number\n"); + exitcode = EXIT_FAILURE; + goto end; + } + port = (uint16_t)tmpmax; + *address = '\0'; + address = connect_to; + ret = start_mandos_communication(address, port, if_index, &mc); + if(ret < 0){ + exitcode = EXIT_FAILURE; + } else { + exitcode = EXIT_SUCCESS; + } + goto end; + } + + if(not debug){ + avahi_set_log_function(empty_log); + } + + /* Initialize the pseudo-RNG for Avahi */ + srand((unsigned int) time(NULL)); + + /* Allocate main Avahi loop object */ + mc.simple_poll = avahi_simple_poll_new(); + if(mc.simple_poll == NULL){ + fprintf(stderr, "Avahi: Failed to create simple poll object.\n"); + exitcode = EXIT_FAILURE; + goto end; + } + + { + AvahiServerConfig config; + /* Do not publish any local Zeroconf records */ + avahi_server_config_init(&config); + config.publish_hinfo = 0; + 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); + } + + /* Check if creating the Avahi server object succeeded */ + if(mc.server == NULL){ + fprintf(stderr, "Failed to create Avahi server: %s\n", + avahi_strerror(error)); + exitcode = EXIT_FAILURE; + goto end; + } + + /* Create the Avahi service browser */ + sb = avahi_s_service_browser_new(mc.server, if_index, + AVAHI_PROTO_INET6, "_mandos._tcp", + NULL, 0, browse_callback, &mc); + if(sb == NULL){ + fprintf(stderr, "Failed to create service browser: %s\n", + avahi_strerror(avahi_server_errno(mc.server))); + exitcode = EXIT_FAILURE; + goto end; + } + + /* Run the main loop */ + + if(debug){ + fprintf(stderr, "Starting Avahi loop search\n"); + } + + avahi_simple_poll_loop(mc.simple_poll); + end: - - if(debug){ - fprintf(stderr, "%s exiting\n", argv[0]); - } - - /* Cleanup things */ - if(sb != NULL) - avahi_s_service_browser_free(sb); - - 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(); - gnutls_dh_params_deinit(mc.dh_params); - } - - if(gpgme_initalized){ - gpgme_release(mc.ctx); - } - - /* Removes the temp directory used by GPGME */ - if(tempdir[0] != '\0'){ - DIR *d; - struct dirent *direntry; - d = opendir(tempdir); - if(d == NULL){ - if(errno != ENOENT){ - perror("opendir"); - } - } else { - while(true){ - direntry = readdir(d); - if(direntry == NULL){ - break; - } - if(direntry->d_type == DT_REG){ - char *fullname = NULL; - ret = asprintf(&fullname, "%s/%s", tempdir, - direntry->d_name); - if(ret < 0){ - perror("asprintf"); - continue; - } - ret = unlink(fullname); - if(ret == -1){ - fprintf(stderr, "unlink(\"%s\"): %s", - fullname, strerror(errno)); - } - free(fullname); - } - } - closedir(d); - } - ret = rmdir(tempdir); - if(ret == -1 and errno != ENOENT){ - perror("rmdir"); - } - } - - return exitcode; + + if(debug){ + fprintf(stderr, "%s exiting\n", argv[0]); + } + + /* Cleanup things */ + if(sb != NULL) + avahi_s_service_browser_free(sb); + + if(mc.server != NULL) + avahi_server_free(mc.server); + + if(mc.simple_poll != NULL) + avahi_simple_poll_free(mc.simple_poll); + + if(gnutls_initialized){ + gnutls_certificate_free_credentials(mc.cred); + gnutls_global_deinit(); + gnutls_dh_params_deinit(mc.dh_params); + } + + if(gpgme_initialized){ + gpgme_release(mc.ctx); + } + + /* Removes the temp directory used by GPGME */ + if(tempdir_created){ + DIR *d; + struct dirent *direntry; + d = opendir(tempdir); + if(d == NULL){ + if(errno != ENOENT){ + perror("opendir"); + } + } else { + while(true){ + direntry = readdir(d); + if(direntry == NULL){ + break; + } + /* Skip "." and ".." */ + if(direntry->d_name[0] == '.' + and (direntry->d_name[1] == '\0' + or (direntry->d_name[1] == '.' + and direntry->d_name[2] == '\0'))){ + continue; + } + char *fullname = NULL; + ret = asprintf(&fullname, "%s/%s", tempdir, + direntry->d_name); + if(ret < 0){ + perror("asprintf"); + continue; + } + ret = remove(fullname); + if(ret == -1){ + fprintf(stderr, "remove(\"%s\"): %s\n", fullname, + strerror(errno)); + } + free(fullname); + } + closedir(d); + } + ret = rmdir(tempdir); + if(ret == -1 and errno != ENOENT){ + perror("rmdir"); + } + } + + return exitcode; } === modified file 'plugins.d/mandos-client.xml' --- plugins.d/mandos-client.xml 2009-01-17 12:12:26 +0000 +++ plugins.d/mandos-client.xml 2009-01-31 10:33:17 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -126,11 +126,16 @@ &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 received. + to get a password. In slightly more detail, this client program + brings up a network interface, uses the interface’s IPv6 + link-local address to get network connectivity, uses Zeroconf to + find servers on the local network, and communicates with servers + using TLS with an OpenPGP key to ensure authenticity and + confidentiality. This client program keeps running, trying all + servers on the network, until it receives a satisfactory reply + or a TERM signal is received. If no servers are found, or after + all servers have been tried, it waits indefinitely for new + servers to appear. This program is not meant to be run directly; it is really meant @@ -205,6 +210,15 @@ specifies the interface to use to connect to the address given. + + Note that since this program will normally run in the + initial RAM disk environment, the interface must be an + interface which exists at that stage. Thus, the interface + can not be a pseudo-interface such as br0 + or tun0; such interfaces will not exist + until much later in the boot process, and can not be used + by this program. + === modified file 'plugins.d/password-prompt.c' --- plugins.d/password-prompt.c 2009-01-17 12:12:26 +0000 +++ plugins.d/password-prompt.c 2009-02-05 02:33:05 +0000 @@ -51,13 +51,13 @@ ARGP_KEY_ARG, ARGP_KEY_END, ARGP_ERR_UNKNOWN */ -volatile bool quit_now = false; +volatile sig_atomic_t quit_now = 0; bool debug = false; const char *argp_program_version = "password-prompt " VERSION; const char *argp_program_bug_address = ""; static void termination_handler(__attribute__((unused))int signum){ - quit_now = true; + quit_now = 1; } int main(int argc, char **argv){ @@ -80,8 +80,8 @@ { .name = NULL } }; - error_t parse_opt (int key, char *arg, struct argp_state *state) { - switch (key) { + error_t parse_opt (int key, char *arg, struct argp_state *state){ + switch (key){ case 'p': prefix = arg; break; @@ -242,8 +242,8 @@ /* if(ret == 0), then the only sensible thing to do is to retry to read from stdin */ fputc('\n', stderr); - if(debug and not quit_now){ - /* If quit_now is true, we were interrupted by a signal, and + if(debug and quit_now == 0){ + /* If quit_now is nonzero, we were interrupted by a signal, and will print that later, so no need to show this too. */ fprintf(stderr, "getline() returned 0, retrying.\n"); }