script-login.c revision c0ebeec35e10d50d979d184f36e74e46764f9484
460N/A/* Copyright (c) 2009-2011 Dovecot authors, see the included COPYING file */
460N/A
460N/A#include "lib.h"
460N/A#include "env-util.h"
460N/A#include "execv-const.h"
460N/A#include "fdpass.h"
460N/A#include "restrict-access.h"
460N/A#include "str.h"
460N/A#include "strescape.h"
460N/A#include "settings-parser.h"
460N/A#include "mail-storage-service.h"
460N/A#include "master-interface.h"
460N/A#include "master-service.h"
460N/A#include "master-service-settings.h"
460N/A
460N/A#include <stdlib.h>
460N/A#include <unistd.h>
460N/A
460N/A#define SCRIPT_LOGIN_PROTOCOL_VERSION_MAJOR 1
460N/A#define SCRIPT_LOGIN_READ_TIMEOUT_SECS 10
460N/A#define ENV_USERDB_KEYS "USERDB_KEYS"
460N/A#define SCRIPT_COMM_FD 3
460N/A
460N/Astatic const char **exec_args;
460N/Astatic bool drop_to_userdb_privileges = FALSE;
460N/A
460N/Astatic void client_connected(struct master_service_connection *conn)
460N/A{
460N/A enum mail_storage_service_flags flags =
460N/A MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS;
460N/A string_t *instr, *keys;
460N/A const char **args, *key, *value, *error, *version_line, *data_line;
460N/A struct mail_storage_service_ctx *service_ctx;
460N/A struct mail_storage_service_input input;
460N/A struct mail_storage_service_user *user;
460N/A char buf[1024];
460N/A unsigned int i, socket_count;
460N/A int fd = -1;
460N/A ssize_t ret;
460N/A
460N/A alarm(SCRIPT_LOGIN_READ_TIMEOUT_SECS);
460N/A
460N/A net_set_nonblock(conn->fd, FALSE);
460N/A instr = t_str_new(1024);
460N/A ret = fd_read(conn->fd, buf, sizeof(buf), &fd);
460N/A while (ret > 0) {
460N/A str_append_n(instr, buf, ret);
460N/A if (buf[ret-1] == '\n' &&
460N/A strchr(str_c(instr), '\n')[1] != '\0') {
460N/A str_truncate(instr, str_len(instr)-1);
460N/A break;
460N/A }
460N/A
460N/A ret = read(conn->fd, buf, sizeof(buf));
460N/A }
460N/A
460N/A version_line = str_c(instr);
460N/A data_line = strchr(version_line, '\n');
460N/A if (data_line != NULL)
460N/A version_line = t_strdup_until(version_line, data_line++);
460N/A else
460N/A version_line = NULL;
460N/A
460N/A if (ret > 0 || version_line != NULL) {
460N/A if (version_line == NULL ||
460N/A !version_string_verify(version_line, "script-login",
460N/A SCRIPT_LOGIN_PROTOCOL_VERSION_MAJOR)) {
460N/A i_fatal("Client not compatible with this binary "
460N/A "(connecting to wrong socket?)");
460N/A }
460N/A }
460N/A
460N/A if (ret <= 0) {
460N/A if (ret < 0)
460N/A i_fatal("read() failed: %m");
460N/A else
460N/A i_fatal("read() failed: disconnected");
460N/A }
460N/A if (fd == -1)
460N/A i_fatal("client fd not received");
460N/A
alarm(0);
/* put everything to environment */
env_clean();
keys = t_str_new(256);
args = t_strsplit(data_line, "\t");
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=", input.username, NULL));
for (; args[i] != '\0'; i++) {
args[i] = str_tabunescape(t_strdup_noconst(args[i]));
value = strchr(args[i], '=');
if (value != NULL) {
key = t_str_ucase(t_strdup_until(args[i], value));
env_put(t_strconcat(key, value, NULL));
str_printfa(keys, "%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);
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 (conn->fd != SCRIPT_COMM_FD) {
if (dup2(conn->fd, SCRIPT_COMM_FD) < 0)
i_fatal("dup2() 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 void script_execute_finish(void)
{
const char *keys_str, *username, *const *keys, *value;
string_t *reply = t_str_new(512);
ssize_t ret;
keys_str = getenv(ENV_USERDB_KEYS);
if (keys_str == NULL)
i_fatal(ENV_USERDB_KEYS" environment missing");
username = getenv("USER");
if (username == NULL)
i_fatal("USER environment missing");
str_append(reply, username);
for (keys = t_strsplit_spaces(keys_str, " "); *keys != NULL; keys++) {
value = getenv(t_str_ucase(*keys));
if (value != NULL) {
str_append_c(reply, '\t');
str_tabescape_write(reply,
t_strconcat(t_str_lcase(*keys), "=",
value, NULL));
}
}
str_append_c(reply, '\n');
ret = fd_send(SCRIPT_COMM_FD, STDOUT_FILENO,
str_data(reply), str_len(reply));
if (ret < 0)
i_fatal("fd_send() failed: %m");
else if (ret != (ssize_t)str_len(reply))
i_fatal("fd_send() sent partial output");
}
int main(int argc, char *argv[])
{
enum master_service_flags flags = 0;
int i, c;
if (getenv(MASTER_IS_PARENT_ENV) == NULL)
flags |= MASTER_SERVICE_FLAG_STANDALONE;
master_service = master_service_init("script-login", flags,
&argc, &argv, "+d");
while ((c = master_getopt(master_service)) > 0) {
switch (c) {
case 'd':
drop_to_userdb_privileges = TRUE;
break;
default:
return FATAL_DEFAULT;
}
}
argc -= optind;
argv += optind;
master_service_init_log(master_service, "script-login: ");
if (!drop_to_userdb_privileges &&
(flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
/* drop to privileges defined by service settings */
restrict_access_by_env(NULL, FALSE);
}
master_service_init_finish(master_service);
master_service_set_service_count(master_service, 1);
if ((flags & MASTER_SERVICE_FLAG_STANDALONE) != 0) {
/* The last post-login script is calling us to finish login */
script_execute_finish();
} else {
if (argv[0] == NULL)
i_fatal("Missing script path");
exec_args = i_new(const char *, argc + 2);
for (i = 0; i < argc; i++)
exec_args[i] = argv[i];
exec_args[i] = PKG_LIBEXECDIR"/script-login";
exec_args[i+1] = NULL;
if (exec_args[0][0] != '/') {
exec_args[0] = t_strconcat(PKG_LIBEXECDIR"/",
exec_args[0], NULL);
}
master_service_run(master_service, client_connected);
}
master_service_deinit(&master_service);
return 0;
}