/* Look up a port using getaddrinfo, and bind one or more sockets. */ static unsigned open_port (struct lshd_context *ctx, struct resource_list *resources, const struct lsh_string *interface, const struct lsh_string *port) { #if HAVE_GETADDRINFO struct addrinfo hints; struct addrinfo *list; struct addrinfo *p; int err; const char *node; unsigned done = 0; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; node = interface ? lsh_get_cstring(interface) : NULL; if (node && !node[0]) node = NULL; debug("open_port: node = %z, port = %S\n", node ? node : "ANY", port); /* FIXME: Also use AI_ADDRCONFIG? */ hints.ai_flags = AI_PASSIVE; err = getaddrinfo(node, lsh_get_cstring(port), &hints, &list); if (err) werror ("getaddrinfo failed for interface %z, port %S: %z\n", node ? node : "ANY", port, gai_strerror(err)); else { for (p = list; p; p = p->ai_next) { if (p->ai_family == AF_INET || p->ai_family == AF_INET6) { struct resource *port = make_lshd_port (ctx, p->ai_addrlen, p->ai_addr); if (port) { remember_resource(resources, port); done++; } } } freeaddrinfo(list); } return done; #else /* !HAVE_GETADDRINFO */ #error getaddrinfo currently required */ #endif /* !HAVE_GETADDRINFO */ }
/* Consumes file name */ struct resource * make_pid_file_resource(struct lsh_string *file) { const char *cname = lsh_get_cstring(file); int fd; assert (cname); /* Try to open the file atomically. This provides sufficient locking * on normal (non-NFS) file systems. */ fd = open(cname, O_WRONLY | O_CREAT | O_EXCL, 0644); if (fd < 0) { if (errno != EEXIST) werror("Failed to open pid file '%S': %e.\n", file, errno); else /* FIXME: We could try to detect and ignore stale pid files. */ werror("Pid file '%S' already exists.\n", file); lsh_string_length(file); return NULL; } else { struct lsh_string *pid = ssh_format("%di", getpid()); if (!write_raw(fd, STRING_LD(pid))) { werror("Writing pidfile `%S' failed: %e\n", file, errno); /* Attempt unlinking file */ if (unlink(cname) < 0) werror("Unlinking pid file '%S' failed: %e.\n", file, errno); close(fd); lsh_string_free(pid); lsh_string_free(file); return NULL; } else { NEW(pid_file_resource, self); init_resource(&self->super, do_kill_pid_file); self->file = file; lsh_string_free(pid); close(fd); return &self->super; } } }
static void do_kill_pid_file(struct resource *s) { CAST(pid_file_resource, self, s); if (self->super.alive) { self->super.alive = 0; if (unlink(lsh_get_cstring(self->file)) < 0) werror("Unlinking pidfile `%S' failed: %e.\n", self->file, errno); } }
/* Bind all appropriate ports for a given interface. */ static unsigned open_interface(struct lshd_context *ctx, struct resource_list *resources, struct lshd_interface *interface, struct string_queue *ports) { debug("open_interface: name = %z, port = %z\n", interface && interface->name ? lsh_get_cstring(interface->name) : "ANY", interface && interface->port ? lsh_get_cstring(interface->port) : "DEFAULT"); if (interface && interface->port) return open_port(ctx, resources, interface->name, interface->port); else { const struct lsh_string *name = interface ? interface->name : NULL; unsigned done = 0; FOR_STRING_QUEUE(ports, port) done += open_port(ctx, resources, name, port); return done; } }
static int write_syslog(int fd UNUSED, uint32_t length, const uint8_t *data) { struct lsh_string *s; /* Data must not contain any NUL:s */ assert(!memchr(data, '\0', length)); /* NUL-terminate the string. */ s = ssh_format("%ls", length, data); /* FIXME: Should we use different log levels for werror, verbose and * debug? */ syslog(LOG_NOTICE, "%s", lsh_get_cstring(s)); lsh_string_free(s); return 1; }
/* Resolve a dns name or a literal IPv4 or IPv6 address */ static void do_resolve(struct command *s, struct lsh_object *x, struct command_continuation *c, struct exception_handler *e) { CAST(resolver_command, self, s); CAST(address_info, a, x); adns_query q; const char *name = lsh_get_cstring(a->host);; assert(!a->socket); /* FIXME: Check literal address first. And fail more gracefully for * names including NUL.*/ assert(name); adns_submit(self->resolver->adns, name, adns_r_addr, 0, /* adns_queryflags, could use IPv6-related flags */ make_resolver_context(a, c, e), &q); }
int main(int argc, char **argv) { struct lsh_decode_key_options *options = make_lsh_decode_key_options(); struct lsh_string *input; struct lsh_string *output; int out = STDOUT_FILENO; argp_parse(&main_argp, argc, argv, 0, NULL, options); if (options->file) { out = open(lsh_get_cstring(options->file), O_WRONLY | O_CREAT, 0666); if (out < 0) { werror("Failed to open file `%S' for writing: %e.\n", options->file, errno); return EXIT_FAILURE; } } input = io_read_file_raw(STDIN_FILENO, 3000); if (!input) { werror("Failed to read stdin: %e.\n", errno); return EXIT_FAILURE; } if (options->base64) { if (!lsh_string_base64_decode(input)) { werror("Invalid base64 encoding.\n"); lsh_string_free(input); return EXIT_FAILURE; } } output = lsh_decode_key(input); lsh_string_free(input); if (!output) { werror("Invalid ssh2 key.\n"); return EXIT_FAILURE; } if (!write_raw(out, STRING_LD(output))) { werror("Write failed: %e.\n", errno); return EXIT_FAILURE; } lsh_string_free(output); gc_final(); return EXIT_SUCCESS; }
static void lshd_service_request_handler(struct transport_forward *self, uint32_t length, const uint8_t *packet) { struct simple_buffer buffer; unsigned msg_number; const uint8_t *name; uint32_t name_length; simple_buffer_init(&buffer, length, packet); if (parse_uint8(&buffer, &msg_number) && (msg_number == SSH_MSG_SERVICE_REQUEST) && parse_string(&buffer, &name_length, &name) && parse_eod(&buffer)) { CAST(lshd_context, ctx, self->super.ctx); const struct service_entry *service = service_config_lookup(ctx->service_config, name_length, name); if (service) { int pipe[2]; pid_t child; if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe) < 0) { werror("lshd_service_request_handler: socketpair failed: %e.\n", errno); transport_disconnect(&self->super, SSH_DISCONNECT_SERVICE_NOT_AVAILABLE, "Service could not be started"); return; } child = fork(); if (child < 0) { werror("lshd_service_request_handler: fork failed: %e.\n", errno); close(pipe[0]); close(pipe[1]); transport_disconnect(&self->super, SSH_DISCONNECT_SERVICE_NOT_AVAILABLE, "Service could not be started"); return; } if (child) { /* Parent process */ close(pipe[1]); transport_send_packet(&self->super, TRANSPORT_WRITE_FLAG_PUSH, format_service_accept(name_length, name)); /* Setup forwarding. Replaces event_handler and packet_handler. */ transport_forward_setup(self, pipe[0], pipe[0]); } else { /* Child process */ struct arglist args; const char *program; unsigned i; close(pipe[0]); dup2(pipe[1], STDIN_FILENO); dup2(pipe[1], STDOUT_FILENO); close(pipe[1]); /* FIXME: Pass sufficient information so that $SSH_CLIENT can be set properly. */ arglist_init (&args); program = service->args.argv[0]; arglist_push (&args, program); /* If not absolute, interpret it relative to libexecdir. */ if (program[0] != '/') program = lsh_get_cstring(ssh_format("%lz/%lz", ctx->service_config->libexec_dir, program)); for (i = 1; i < service->args.argc; i++) { const char *arg = service->args.argv[i]; if (arg[0] == '$') { if (!strcmp(arg+1, "(session_id)")) arg = lsh_get_cstring(ssh_format("%lxS", self->super.session_id)); } arglist_push (&args, arg); } debug("exec of service %s, program %z. Argument list:\n", name_length, name, program); for (i = 0; i < args.argc; i++) debug(" %z\n", args.argv[i]); execv(program, (char **) args.argv); werror("lshd_service_request_handler: exec of %z failed: %e.\n", args.argv[0], errno); _exit(EXIT_FAILURE); } } else transport_disconnect(&self->super, SSH_DISCONNECT_SERVICE_NOT_AVAILABLE, "Service not available"); } else transport_protocol_error(&self->super, "Invalid SERVICE_REQUEST"); }
int test_main(void) { struct lsh_string *s; const char *p; s = S("foo"); p = lsh_get_cstring(s); ASSERT(p && !strcmp(p, "foo")) ; s = H("66006f"); ASSERT(!lsh_get_cstring(s)); s = H("6600"); ASSERT(!lsh_get_cstring(s)); s = H(""); p = lsh_get_cstring(s); ASSERT(p && !*p); s = S("colonize this"); s = lsh_string_colonize(s, 2, 1); p = lsh_get_cstring(s); ASSERT(p && !strcmp(p, "co:lo:ni:ze: t:hi:s")) ; s = S("fo"); s = lsh_string_colonize(s, 2, 1); p = lsh_get_cstring(s); ASSERT(p && !strcmp(p, "fo")) ; s = S(""); s = lsh_string_colonize(s, 1, 1); p = lsh_get_cstring(s); ASSERT(p && !*p) ; s = S("colonize this"); s = lsh_string_colonize( s, 1, 1); p = lsh_get_cstring(s); ASSERT(p && !strcmp(p, "c:o:l:o:n:i:z:e: :t:h:i:s")) ; s = S(""); s = lsh_string_bubblebabble(s, 1); p = lsh_get_cstring(s); ASSERT(p && !strcmp(p, "xexax")) ; s = S("1234567890"); s = lsh_string_bubblebabble(s, 1); p = lsh_get_cstring(s); ASSERT(p && !strcmp(p, "xesef-disof-gytuf-katof-movif-baxux")) ; s = S("Pineapple"); s = lsh_string_bubblebabble(s, 1); p = lsh_get_cstring(s); ASSERT(p && !strcmp(p, "xigak-nyryk-humil-bosek-sonax")) ; /* This should at least strigger one typo I made in the bubble babble routine */ s = S("\xb8\x4e\xce\x86\x7b\x92\x8e\xf2\xda\x8e\xee\x15\xc2\x5d\xac\xd6\xe8\x6c\xb3\x43"); s = lsh_string_bubblebabble(s, 1); p = lsh_get_cstring(s); ASSERT(p && !strcmp(p, "xovag-vafim-kivun-dafez-dykim-veryc-habeh-turyt-kopyk-sasug-fixax")) ; SUCCESS(); }
static error_t main_argp_parser(int key, char *arg, struct argp_state *state) { CAST(lsh_writekey_options, self, state->input); switch(key) { default: return ARGP_ERR_UNKNOWN; case ARGP_KEY_INIT: state->child_inputs[0] = &self->style; state->child_inputs[1] = NULL; break; case ARGP_KEY_END: if (!self->file) { char *home = getenv("HOME"); struct lsh_string *s; if (!home) { argp_failure(state, EXIT_FAILURE, 0, "$HOME not set."); return EINVAL; } else { #ifndef MACOS s = ssh_format("%lz/.lsh", home); if (mkdir(lsh_get_cstring(s), 0755) < 0) { if (errno != EEXIST) argp_failure(state, EXIT_FAILURE, errno, "Creating directory %s failed.", s->data); } lsh_string_free(s); self->file = ssh_format("%lz/.lsh/identity", home); #else self->file = ssh_format("%lzidentity", home); #endif } } if (self->crypto) { if (!self->label) { const char *name = getenv("LOGNAME"); #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 300 #endif char host[MAXHOSTNAMELEN]; if (!name) { argp_failure(state, EXIT_FAILURE, 0, "LOGNAME not set. Please use the -l option."); return EINVAL; } if ( (gethostname(host, sizeof(host)) < 0) && (errno != ENAMETOOLONG) ) argp_failure(state, EXIT_FAILURE, errno, "Can't get the host name. Please use the -l option."); self->label = ssh_format("%lz@%lz", name, host); } while (!self->passphrase) { struct lsh_string *pw; struct lsh_string *again; pw = INTERACT_READ_PASSWORD(self->tty, 500, ssh_format("Enter new passphrase: "), 1); if (!pw) argp_failure(state, EXIT_FAILURE, 0, "Aborted."); again = INTERACT_READ_PASSWORD(self->tty, 500, ssh_format("Again: "), 1); if (!again) argp_failure(state, EXIT_FAILURE, 0, "Aborted."); if (lsh_string_eq(pw, again)) self->passphrase = pw; else lsh_string_free(pw); lsh_string_free(again); } } break; case 'o': self->file = make_string(arg); break; case 'i': { long i; char *end; i = strtol(arg, &end, 0); if ((end == arg) || *end || (i < 1)) { argp_failure(state, EXIT_FAILURE, 0, "Invalid iteration count."); return EINVAL; } else if (i > PKCS5_MAX_ITERATIONS) { argp_error(state, "Iteration count ridiculously large (> %d).", PKCS5_MAX_ITERATIONS); return EINVAL; } else self->iterations = i; break; } case 'c': { int name = lookup_crypto(self->crypto_algorithms, arg, &self->crypto); if (name) self->crypto_name = name; else { list_crypto_algorithms(state, self->crypto_algorithms); argp_error(state, "Unknown crypto algorithm '%s'.", arg); } break; } case 'l': self->label = ssh_format("%lz", arg); break; case 'p': self->passphrase = ssh_format("%lz", arg); break; } return 0; }