const char *t_str_tabunescape(const char *str) { if (strchr(str, '\001') == NULL) return str; else return str_tabunescape(t_strdup_noconst(str)); }
static void stream_data(string_t *str, const unsigned char *data, size_t size) { const char *text; str_truncate(str, 0); str_append_n(str, data, size); text = str_tabunescape(str_c_modifiable(str)); doveadm_print_stream(text, strlen(text)); }
static void server_flush_field(struct server_connection *conn, string_t *str, const unsigned char *data, size_t size) { if (conn->streaming) { conn->streaming = FALSE; if (size > 0) stream_data(str, data, size); doveadm_print_stream("", 0); } else { const char *text; str_truncate(str, 0); str_append_n(str, data, size); text = str_tabunescape(str_c_modifiable(str)); doveadm_print(text); } }
static bool client_handle_command(struct client_connection *conn, char **args) { struct mail_storage_service_input input; struct doveadm_mail_cmd_context *ctx; const char *flags, *cmd_name; unsigned int argc; memset(&input, 0, sizeof(input)); input.service = "doveadm"; for (argc = 0; args[argc] != NULL; argc++) args[argc] = str_tabunescape(args[argc]); if (argc < 3) { i_error("doveadm client: No command given"); return FALSE; } flags = args[0]; input.username = args[1]; cmd_name = args[2]; /* leave the command name as args[0] so getopt() works */ args += 2; argc -= 2; doveadm_debug = FALSE; doveadm_verbose = FALSE; for (; *flags != '\0'; flags++) { switch (*flags) { case 'D': doveadm_debug = TRUE; doveadm_verbose = TRUE; break; case 'v': doveadm_verbose = TRUE; break; default: i_error("doveadm client: Unknown flag: %c", *flags); return FALSE; } } if (!client_is_allowed_command(conn->set, cmd_name)) { i_error("doveadm client isn't allowed to use command: %s", cmd_name); return FALSE; } /* make sure client_connection_input() isn't called by the ioloop that is going to be run by doveadm_mail_cmd_server_run() */ io_remove(&conn->io); o_stream_cork(conn->output); ctx = doveadm_mail_cmd_server_parse(cmd_name, conn->set, &input, argc, args); if (ctx == NULL) o_stream_nsend(conn->output, "\n-\n", 3); else doveadm_mail_cmd_server_run(conn, ctx, &input); o_stream_uncork(conn->output); /* flush the output and disconnect */ net_set_nonblock(conn->fd, FALSE); (void)o_stream_flush(conn->output); net_set_nonblock(conn->fd, TRUE); conn->io = io_add(conn->fd, IO_READ, client_connection_input, conn); return TRUE; }
static void client_connected(struct master_service_connection *conn) { enum mail_storage_service_flags flags = MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS; string_t *instr, *keys; const char **args, *key, *value, *error, *version_line, *data_line; struct mail_storage_service_ctx *service_ctx; struct mail_storage_service_input input; struct mail_storage_service_user *user; char buf[1024]; unsigned int i, socket_count; int fd = -1; ssize_t ret; alarm(SCRIPT_LOGIN_READ_TIMEOUT_SECS); net_set_nonblock(conn->fd, FALSE); instr = t_str_new(1024); ret = fd_read(conn->fd, buf, sizeof(buf), &fd); while (ret > 0) { str_append_n(instr, buf, ret); if (buf[ret-1] == '\n' && strchr(str_c(instr), '\n')[1] != '\0') { str_truncate(instr, str_len(instr)-1); break; } ret = read(conn->fd, buf, sizeof(buf)); } version_line = str_c(instr); data_line = strchr(version_line, '\n'); if (data_line != NULL) version_line = t_strdup_until(version_line, data_line++); else version_line = NULL; if (ret > 0 || version_line != NULL) { if (version_line == NULL || !version_string_verify(version_line, "script-login", SCRIPT_LOGIN_PROTOCOL_VERSION_MAJOR)) { i_fatal("Client not compatible with this binary " "(connecting to wrong socket?)"); } } if (ret <= 0) { if (ret < 0) i_fatal("read() failed: %m"); else i_fatal("read() failed: disconnected"); } if (fd == -1) i_fatal("client fd not received"); alarm(0); /* put everything to environment */ env_clean(); keys = t_str_new(256); args = t_strsplit_tab(data_line); if (str_array_length(args) < 3) i_fatal("Missing input fields"); i = 0; memset(&input, 0, sizeof(input)); input.module = "mail"; /* need to get mail_uid, mail_gid */ input.service = "script-login"; (void)net_addr2ip(args[i++], &input.local_ip); (void)net_addr2ip(args[i++], &input.remote_ip); input.username = args[i++]; input.userdb_fields = args + i; env_put(t_strconcat("LOCAL_IP=", net_ip2addr(&input.local_ip), NULL)); env_put(t_strconcat("IP=", net_ip2addr(&input.remote_ip), NULL)); env_put(t_strconcat("USER="******"%s ", key); } } env_put(t_strconcat(ENV_USERDB_KEYS"=", str_c(keys), NULL)); master_service_init_log(master_service, t_strdup_printf("script-login(%s): ", input.username)); if (drop_to_userdb_privileges) { service_ctx = mail_storage_service_init(master_service, NULL, flags); if (mail_storage_service_lookup(service_ctx, &input, &user, &error) <= 0) i_fatal("%s", error); mail_storage_service_restrict_setenv(service_ctx, user); /* we can't exec anything in a chroot */ env_remove("RESTRICT_CHROOT"); restrict_access_by_env(getenv("HOME"), TRUE); } if (dup2(fd, STDIN_FILENO) < 0) i_fatal("dup2() failed: %m"); if (dup2(fd, STDOUT_FILENO) < 0) i_fatal("dup2() failed: %m"); if (close(fd) < 0) i_fatal("close() failed: %m"); if (conn->fd != SCRIPT_COMM_FD) { if (dup2(conn->fd, SCRIPT_COMM_FD) < 0) i_fatal("dup2() failed: %m"); if (close(conn->fd) < 0) i_fatal("close() failed: %m"); } /* close all listener sockets */ socket_count = master_service_get_socket_count(master_service); for (i = 0; i < socket_count; i++) { if (close(MASTER_LISTEN_FD_FIRST + i) < 0) i_error("close(listener) failed: %m"); } if (close(MASTER_STATUS_FD) < 0) i_error("close(status) failed: %m"); execvp_const(exec_args[0], exec_args); }
static bool client_handle_command(struct client_connection *conn, char **args) { struct mail_storage_service_input input; const char *flags, *cmd_name; unsigned int argc; memset(&input, 0, sizeof(input)); input.service = "doveadm"; input.local_ip = conn->local_ip; input.remote_ip = conn->remote_ip; input.local_port = conn->local_port; input.remote_port = conn->remote_port; for (argc = 0; args[argc] != NULL; argc++) args[argc] = str_tabunescape(args[argc]); if (argc < 3) { i_error("doveadm client: No command given"); return FALSE; } flags = args[0]; input.username = args[1]; cmd_name = args[2]; /* leave the command name as args[0] so getopt() works */ args += 2; argc -= 2; doveadm_debug = FALSE; doveadm_verbose = FALSE; for (; *flags != '\0'; flags++) { switch (*flags) { case 'D': doveadm_debug = TRUE; doveadm_verbose = TRUE; break; case 'v': doveadm_verbose = TRUE; break; default: i_error("doveadm client: Unknown flag: %c", *flags); return FALSE; } } if (!client_is_allowed_command(conn->set, cmd_name)) { i_error("doveadm client isn't allowed to use command: %s", cmd_name); return FALSE; } o_stream_cork(conn->output); if (doveadm_cmd_handle(conn, cmd_name, &input, argc, args) < 0) o_stream_nsend(conn->output, "\n-\n", 3); o_stream_uncork(conn->output); /* flush the output and disconnect */ net_set_nonblock(conn->fd, FALSE); (void)o_stream_flush(conn->output); net_set_nonblock(conn->fd, TRUE); return TRUE; }
void test_strescape(void) { static struct strinput unesc[] = { { "foo", "foo" }, { "\\\\\\\\\\\"\\\"\\\'\\\'", "\\\\\"\"\'\'" }, { "\\a\\n\\r\\", "anr" } }; static struct strinput tabesc[] = { { "foo", "foo" }, { "\001", "\0011" }, { "\t", "\001t" }, { "\r", "\001r" }, { "\n", "\001n" }, { "\001\001\t\t\r\r\n\n", "\0011\0011\001t\001t\001r\001r\001n\001n" } }; unsigned char buf[1 << CHAR_BIT]; const char *escaped, *tabstr; string_t *str; unsigned int i; test_begin("str_escape"); for (i = 1; i < sizeof(buf); i++) buf[i-1] = i; buf[i-1] = '\0'; escaped = str_escape((char *)buf); test_assert(strlen(escaped) == (1 << CHAR_BIT) - 1 + 3); test_assert(escaped['\"'-1] == '\\'); /* 34 */ test_assert(escaped['\"'] == '\"'); test_assert(escaped['\''+1-1] == '\\'); /* 39 */ test_assert(escaped['\''+1] == '\''); test_assert(escaped['\\'+2-1] == '\\'); /* 92 */ test_assert(escaped['\\'+2] == '\\'); test_assert(strcmp(str_escape("\\\\\"\"\'\'"), "\\\\\\\\\\\"\\\"\\\'\\\'") == 0); test_end(); str = t_str_new(256); test_begin("str_unescape"); for (i = 0; i < N_ELEMENTS(unesc); i++) { test_assert(strcmp(str_unescape(t_strdup_noconst(unesc[i].input)), unesc[i].output) == 0); str_truncate(str, 0); str_append_unescaped(str, unesc[i].input, strlen(unesc[i].input)); test_assert(strcmp(str_c(str), unesc[i].output) == 0); } test_end(); test_begin("str_tabescape"); for (i = 0; i < N_ELEMENTS(tabesc); i++) { test_assert(strcmp(str_tabunescape(t_strdup_noconst(tabesc[i].output)), tabesc[i].input) == 0); test_assert(strcmp(str_tabescape(tabesc[i].input), tabesc[i].output) == 0); str_truncate(str, 0); str_append_tabunescaped(str, tabesc[i].output, strlen(tabesc[i].output)); test_assert(strcmp(str_c(str), tabesc[i].input) == 0); } str_truncate(str, 0); tabstr = "\0012\001l\001"; str_append_tabunescaped(str, tabstr, strlen(tabstr)); test_assert(strcmp(str_c(str), "2l") == 0); test_assert(strcmp(str_c(str), str_tabunescape(t_strdup_noconst(tabstr))) == 0); test_end(); }