# passdb = passwd-file /etc/passwd.imap
# user = dovecot-auth
#}
+
+# It's possible to export the authentication interface to other programs,
+# for example SMTP server which supports talking to Dovecot. Client socket
+# handles the actual authentication - you give it a username and password
+# and it returns OK or failure. So it's pretty safe to allow anyone access to
+# it. Master socket is used to a) query if given client was successfully
+# authenticated, b) userdb lookups.
+
+# listener sockets will be created by Dovecot's master process using the
+# settings given inside the auth section
+#auth default_with_listener {
+# mechanisms = plain
+# passdb = passwd
+# userdb = pam
+# socket listen {
+# master {
+# path = /var/run/dovecot/auth-master
+# #mode = 0600
+# # Default user/group is the one who started dovecot-auth (root)
+# #user =
+# #group =
+# }
+# client {
+# path = /var/run/dovecot-auth-client
+# mode = 0660
+# }
+# }
+#}
+
+# connect sockets are assumed to be already running, Dovecot's master
+# process only tries to connect to them. They don't need any other settings
+# than path for the master socket, as the configuration is done elsewhere.
+# Note that the client sockets must exist in login_dir.
+#auth external {
+# socket connect {
+# master {
+# path = /var/run/dovecot/auth-master
+# }
+# }
+#}
static struct auth_master_reply failure_reply =
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+struct auth_listener {
+ struct auth_master_connection *master;
+ int client_listener;
+ int fd;
+ char *path;
+ struct io *io;
+};
+
struct master_userdb_request {
struct auth_master_connection *conn;
unsigned int tag;
};
+static void auth_master_connection_close(struct auth_master_connection *conn);
static int auth_master_connection_unref(struct auth_master_connection *conn);
static size_t reply_add(buffer_t *buf, const char *str)
ret = o_stream_send(conn->output, reply, reply_size);
if (ret < 0) {
/* master died, kill ourself too */
- io_loop_stop(ioloop);
+ auth_master_connection_close(conn);
break;
}
i_warning("Master transmit buffer full, blocking..");
if (o_stream_flush(conn->output) < 0) {
/* transmit error, probably master died */
- io_loop_stop(ioloop);
+ auth_master_connection_close(conn);
break;
}
}
sizeof(conn->request_buf) - conn->request_pos);
if (ret < 0) {
/* master died, kill ourself too */
- io_loop_stop(ioloop);
+ auth_master_connection_close(conn);
return;
}
master->handshake_reply = buffer_free_without_data(buf);
}
+static void
+auth_master_connection_set_fd(struct auth_master_connection *conn, int fd)
+{
+ if (conn->output != NULL)
+ o_stream_unref(conn->output);
+ if (conn->io != NULL)
+ io_remove(conn->io);
+
+ conn->output = o_stream_create_file(fd, default_pool,
+ MAX_OUTBUF_SIZE, FALSE);
+ conn->io = io_add(fd, IO_READ, master_input, conn);
+
+ conn->fd = fd;
+}
+
struct auth_master_connection *
-auth_master_connection_new(int fd, unsigned int pid)
+auth_master_connection_create(int fd, unsigned int pid)
{
struct auth_master_connection *conn;
conn->fd = fd;
conn->listeners_buf =
buffer_create_dynamic(default_pool, 64, (size_t)-1);
- if (fd != -1) {
- conn->output = o_stream_create_file(fd, default_pool,
- MAX_OUTBUF_SIZE, FALSE);
- conn->io = io_add(fd, IO_READ, master_input, conn);
- }
+ if (fd != -1)
+ auth_master_connection_set_fd(conn, fd);
master_get_handshake_reply(conn);
return conn;
}
void auth_master_connection_send_handshake(struct auth_master_connection *conn)
{
+ struct auth_master_handshake_reply reply;
+
/* just a note to master that we're ok. if we die before,
master should shutdown itself. */
- if (conn->output != NULL)
- o_stream_send(conn->output, "O", 1);
+ if (conn->output != NULL) {
+ memset(&reply, 0, sizeof(reply));
+ reply.server_pid = conn->pid;
+ o_stream_send(conn->output, &reply, sizeof(reply));
+ }
+}
+
+static void auth_master_connection_close(struct auth_master_connection *conn)
+{
+ if (!standalone)
+ io_loop_stop(ioloop);
+
+ if (close(conn->fd) < 0)
+ i_error("close(): %m");
+ conn->fd = -1;
+
+ o_stream_close(conn->output);
+ conn->output = NULL;
+
+ io_remove(conn->io);
+ conn->io = NULL;
}
-void auth_master_connection_free(struct auth_master_connection *conn)
+void auth_master_connection_destroy(struct auth_master_connection *conn)
{
- struct auth_client_listener **l;
+ struct auth_listener **l;
size_t i, size;
if (conn->destroyed)
return;
conn->destroyed = TRUE;
- if (conn->fd != -1) {
- if (close(conn->fd) < 0)
- i_error("close(): %m");
- conn->fd = -1;
+ auth_client_connections_deinit(conn);
- o_stream_close(conn->output);
-
- io_remove(conn->io);
- conn->io = NULL;
- }
+ if (conn->fd != -1)
+ auth_master_connection_close(conn);
l = buffer_get_modifyable_data(conn->listeners_buf, &size);
size /= sizeof(*l);
static void auth_accept(void *context)
{
- struct auth_client_listener *l = context;
+ struct auth_listener *l = context;
int fd;
fd = net_accept(l->fd, NULL, NULL);
i_fatal("accept() failed: %m");
} else {
net_set_nonblock(fd, TRUE);
- (void)auth_client_connection_create(l->master, fd);
+ if (l->client_listener)
+ (void)auth_client_connection_create(l->master, fd);
+ else {
+ /* we'll just replace the previous master.. */
+ auth_master_connection_set_fd(l->master, fd);
+ auth_master_connection_send_handshake(l->master);
+ }
}
}
void auth_master_connection_add_listener(struct auth_master_connection *conn,
- int fd, const char *path)
+ int fd, const char *path, int client)
{
- struct auth_client_listener *l;
+ struct auth_listener *l;
- l = i_new(struct auth_client_listener, 1);
+ l = i_new(struct auth_listener, 1);
l->master = conn;
+ l->client_listener = client;
l->fd = fd;
l->path = i_strdup(path);
l->io = io_add(fd, IO_READ, auth_accept, l);
unsigned int destroyed:1;
};
-struct auth_client_listener {
- struct auth_master_connection *master;
- int fd;
- char *path;
- struct io *io;
-};
-
#define AUTH_MASTER_IS_DUMMY(master) (master->fd == -1)
struct auth_master_connection *
-auth_master_connection_new(int fd, unsigned int pid);
+auth_master_connection_create(int fd, unsigned int pid);
void auth_master_connection_send_handshake(struct auth_master_connection *conn);
-void auth_master_connection_free(struct auth_master_connection *conn);
+void auth_master_connection_destroy(struct auth_master_connection *conn);
void auth_master_connection_add_listener(struct auth_master_connection *conn,
- int fd, const char *path);
+ int fd, const char *path, int client);
#endif
#define AUTH_MASTER_MAX_REPLY_DATA_SIZE 4096
+/* Server -> Master */
+struct auth_master_handshake_reply {
+ unsigned int server_pid;
+};
+
struct auth_master_request {
unsigned int tag;
extern struct ioloop *ioloop;
extern int verbose, verbose_debug;
+extern int standalone;
#endif
#include "auth-master-connection.h"
#include "auth-client-connection.h"
+#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/stat.h>
struct ioloop *ioloop;
int verbose = FALSE, verbose_debug = FALSE;
+int standalone = FALSE;
static buffer_t *masters_buf;
restrict_access_by_env(FALSE);
}
-static void master_add_unix_listeners(struct auth_master_connection *master,
- const char *sockets_list)
+static uid_t get_uid(const char *user)
{
- const char *const *sockets;
- int fd;
-
- sockets = t_strsplit(sockets_list, ":");
- while (*sockets != NULL) {
- fd = net_listen_unix(*sockets);
- if (fd == -1) {
- i_fatal("net_listen_unix(%s) failed: %m",
- *sockets);
+ struct passwd *pw;
+
+ if (user == NULL)
+ return (uid_t)-1;
+
+ if ((pw = getpwnam(user)) == NULL)
+ i_fatal("User doesn't exist: %s", user);
+ return pw->pw_uid;
+}
+
+static gid_t get_gid(const char *group)
+{
+ struct group *gr;
+
+ if (group == NULL)
+ return (gid_t)-1;
+
+ if ((gr = getgrnam(group)) == NULL)
+ i_fatal("Group doesn't exist: %s", group);
+ return gr->gr_gid;
+}
+
+static int create_unix_listener(const char *env)
+{
+ const char *path, *mode, *user, *group;
+ mode_t old_umask;
+ unsigned int mask;
+ uid_t uid;
+ gid_t gid;
+ int fd, i;
+
+ path = getenv(env);
+ if (path == NULL)
+ return -1;
+
+ mode = getenv(t_strdup_printf("%s_MODE", env));
+ if (mode == NULL)
+ mask = 0177; /* default to 0600 */
+ else {
+ if (sscanf(mode, "%o", &mask) != 1)
+ i_fatal("%s: Invalid mode %s", env, mode);
+ mask = (mask ^ 0777) & 0777;
+ }
+
+ old_umask = umask(mask);
+ for (i = 0; i < 5; i++) {
+ fd = net_listen_unix(path);
+ if (fd != -1)
+ break;
+
+ if (errno != EADDRINUSE)
+ i_fatal("net_listen_unix(%s) failed: %m", path);
+
+ /* see if it really exists */
+ if (net_connect_unix(path) != -1 || errno != ECONNREFUSED)
+ i_fatal("Socket already exists: %s", path);
+
+ /* delete and try again */
+ if (unlink(path) < 0)
+ i_fatal("unlink(%s) failed: %m", path);
+ }
+ umask(old_umask);
+
+ user = getenv(t_strdup_printf("%s_USER", env));
+ group = getenv(t_strdup_printf("%s_GROUP", env));
+
+ uid = get_uid(user); gid = get_gid(group);
+ if (chown(path, uid, gid) < 0) {
+ i_fatal("chown(%s, %s, %s) failed: %m",
+ path, dec2str(uid), dec2str(gid));
+ }
+
+ return fd;
+}
+
+static void add_extra_listeners(void)
+{
+ struct auth_master_connection *master;
+ const char *str, *client_path, *master_path;
+ int client_fd, master_fd;
+ unsigned int i;
+
+ for (i = 1;; i++) {
+ t_push();
+ client_path = getenv(t_strdup_printf("AUTH_%u", i));
+ master_path = getenv(t_strdup_printf("AUTH_%u_MASTER", i));
+ if (client_path == NULL && master_path == NULL) {
+ t_pop();
+ break;
}
- auth_master_connection_add_listener(master, fd, *sockets);
- sockets++;
+ str = t_strdup_printf("AUTH_%u", i);
+ client_fd = create_unix_listener(str);
+ str = t_strdup_printf("AUTH_%u_MASTER", i);
+ master_fd = create_unix_listener(str);
+
+ master = auth_master_connection_create(-1, getpid());
+ if (master_fd != -1) {
+ auth_master_connection_add_listener(master, master_fd,
+ master_path, FALSE);
+ }
+ if (client_fd != -1) {
+ auth_master_connection_add_listener(master, client_fd,
+ client_path, TRUE);
+ }
+ auth_client_connections_init(master);
+ buffer_append(masters_buf, &master, sizeof(master));
+ t_pop();
}
}
-static void main_init(void)
+static void main_init(int nodaemon)
{
struct auth_master_connection *master, **master_p;
size_t i, size;
masters_buf = buffer_create_dynamic(default_pool, 64, (size_t)-1);
env = getenv("AUTH_PROCESS");
- if (env == NULL) {
+ standalone = env == NULL;
+ if (standalone) {
/* starting standalone */
- env = getenv("AUTH_SOCKETS");
- if (env == NULL)
- i_fatal("AUTH_SOCKETS environment not set");
-
- switch (fork()) {
- case -1:
- i_fatal("fork() failed: %m");
- case 0:
- break;
- default:
- exit(0);
+ if (getenv("AUTH_1") == NULL) {
+ i_fatal("dovecot-auth is usually started through "
+ "dovecot master process. If you wish to run "
+ "it standalone, you'll need to set AUTH_* "
+ "environment variables (AUTH_1 isn't set).");
}
- if (setsid() < 0)
- i_fatal("setsid() failed: %m");
-
- if (chdir("/") < 0)
- i_fatal("chdir(/) failed: %m");
+ if (!nodaemon) {
+ switch (fork()) {
+ case -1:
+ i_fatal("fork() failed: %m");
+ case 0:
+ break;
+ default:
+ exit(0);
+ }
+
+ if (setsid() < 0)
+ i_fatal("setsid() failed: %m");
+
+ if (chdir("/") < 0)
+ i_fatal("chdir(/) failed: %m");
+ }
} else {
pid = atoi(env);
if (pid == 0)
i_fatal("AUTH_PROCESS can't be 0");
- master = auth_master_connection_new(MASTER_SOCKET_FD, pid);
+ master = auth_master_connection_create(MASTER_SOCKET_FD, pid);
auth_master_connection_add_listener(master, LOGIN_LISTEN_FD,
- NULL);
+ NULL, TRUE);
auth_client_connections_init(master);
buffer_append(masters_buf, &master, sizeof(master));
-
- /* accept also alternative listeners under dummy master */
- env = getenv("AUTH_SOCKETS");
}
- if (env != NULL && *env != '\0') {
- master = auth_master_connection_new(-1, 0);
- master_add_unix_listeners(master, env);
- auth_client_connections_init(master);
- buffer_append(masters_buf, &master, sizeof(master));
- }
+ add_extra_listeners();
/* everything initialized, notify masters that all is well */
master_p = buffer_get_modifyable_data(masters_buf, &size);
master = buffer_get_modifyable_data(masters_buf, &size);
size /= sizeof(*master);
- for (i = 0; i < size; i++) {
- auth_client_connections_deinit(master[i]);
- auth_master_connection_free(master[i]);
- }
+ for (i = 0; i < size; i++)
+ auth_master_connection_destroy(master[i]);
password_schemes_deinit();
passdb_deinit();
closelog();
}
-int main(int argc __attr_unused__, char *argv[] __attr_unused__)
+int main(int argc, char *argv[])
{
#ifdef DEBUG
- fd_debug_verify_leaks(4, 1024);
+ if (getenv("GDB") == NULL)
+ fd_debug_verify_leaks(4, 1024);
#endif
/* NOTE: we start rooted, so keep the code minimal until
restrict_access_by_env() is called */
ioloop = io_loop_create(system_pool);
- main_init();
+ main_init(argc > 1 && strcmp(argv[1], "-F") == 0);
io_loop_run(ioloop);
main_deinit();
struct hash_table *requests;
+ unsigned int external:1;
unsigned int initialized:1;
unsigned int in_auth_reply:1;
};
}
if (!p->initialized) {
+ struct auth_master_handshake_reply rec;
+
data = i_stream_get_data(p->input, &size);
- i_assert(size > 0);
+ if (size < sizeof(rec))
+ return;
+
+ memcpy(&rec, data, sizeof(rec));
+ i_stream_skip(p->input, sizeof(rec));
- if (data[0] != 'O') {
+ if (rec.server_pid == 0) {
i_fatal("Auth process sent invalid initialization "
"notification");
}
- i_stream_skip(p->input, 1);
-
+ p->pid = rec.server_pid;
p->initialized = TRUE;
}
{
struct auth_process *p;
- PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_AUTH);
+ if (pid != 0)
+ PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_AUTH);
p = i_new(struct auth_process, 1);
p->group = group;
void *key, *value;
struct auth_process **pos;
- if (!p->initialized && io_loop_is_running(ioloop)) {
+ if (!p->initialized && io_loop_is_running(ioloop) && !p->external) {
i_error("Auth process died too early - shutting down");
io_loop_stop(ioloop);
}
i_free(p);
}
-static pid_t create_auth_process(struct auth_process_group *group)
+static void
+socket_settings_env_put(const char *env_base, struct socket_settings *set)
+{
+ if (env_base == NULL)
+ return;
+
+ env_put(t_strdup_printf("%s_PATH=%s", env_base, set->path));
+ if (set->mode != 0)
+ env_put(t_strdup_printf("%s_MODE=%u", env_base, set->mode));
+ if (set->user != NULL)
+ env_put(t_strdup_printf("%s_USER=%s", env_base, set->user));
+ if (set->group != NULL)
+ env_put(t_strdup_printf("%s_GROUP=%s", env_base, set->group));
+}
+
+static int connect_auth_socket(struct auth_process_group *group,
+ const char *path)
+{
+ struct auth_process *auth;
+ int fd;
+
+ fd = net_connect_unix(path);
+ if (fd == -1) {
+ i_error("net_connect_unix(%s) failed: %m", path);
+ return -1;
+ }
+
+ net_set_nonblock(fd, TRUE);
+ fd_close_on_exec(fd, TRUE);
+ auth = auth_process_new(0, fd, group);
+ auth->external = TRUE;
+ return 0;
+}
+
+static int create_auth_process(struct auth_process_group *group)
{
static char *argv[] = { NULL, NULL };
- const char *prefix;
+ struct auth_socket_settings *as;
+ const char *prefix, *str;
struct log_io *log;
pid_t pid;
int fd[2], log_fd, i;
+ /* see if this is a connect socket */
+ as = group->set->sockets;
+ if (as != NULL && strcmp(as->type, "connect") == 0)
+ return connect_auth_socket(group, as->master.path);
+
/* create communication to process with a socket pair */
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
i_error("socketpair() failed: %m");
auth_process_new(pid, fd[0], group);
(void)close(fd[1]);
(void)close(log_fd);
- return pid;
+ return 0;
}
prefix = t_strdup_printf("master-auth(%s): ", group->set->name);
env_put(t_strconcat("USERNAME_CHARS=", group->set->username_chars, NULL));
env_put(t_strconcat("ANONYMOUS_USERNAME=",
group->set->anonymous_username, NULL));
- env_put(t_strconcat("AUTH_SOCKETS=", group->set->extra_sockets));
+
+ for (as = group->set->sockets, i = 1; as != NULL; as = as->next, i++) {
+ if (strcmp(as->type, "listen") != 0)
+ continue;
+
+ str = t_strdup_printf("AUTH_%u", i);
+ socket_settings_env_put(str, &as->client);
+ socket_settings_env_put(t_strconcat(str, "_MASTER", NULL),
+ &as->master);
+ }
if (group->set->use_cyrus_sasl)
env_put("USE_CYRUS_SASL=1");
group = i_new(struct auth_process_group, 1);
group->set = auth_set;
+ group->next = process_groups;
+ process_groups = group;
+
+ if (auth_set->sockets != NULL &&
+ strcmp(auth_set->sockets->type, "connect") == 0)
+ return;
+
/* create socket for listening auth requests from login */
path = t_strconcat(auth_set->parent->defaults->login_dir, "/",
auth_set->name, NULL);
path, dec2str(master_uid),
dec2str(auth_set->parent->login_gid));
}
-
- group->next = process_groups;
- process_groups = group;
}
static void auth_process_group_destroy(struct auth_process_group *group)
SETTINGS_TYPE_ROOT,
SETTINGS_TYPE_SERVER,
SETTINGS_TYPE_AUTH,
- SETTINGS_TYPE_NAMESPACE
+ SETTINGS_TYPE_AUTH_SOCKET,
+ SETTINGS_TYPE_NAMESPACE,
+ SETTINGS_TYPE_SOCKET
};
struct settings_parse_ctx {
struct server_settings *root, *server;
struct auth_settings *auth;
+ struct socket_settings *socket;
+ struct auth_socket_settings *auth_socket;
struct namespace_settings *namespace;
int level;
DEF(SET_INT, count),
DEF(SET_INT, process_size),
- DEF(SET_STR, extra_sockets),
+
+ { 0, NULL, 0 }
+};
+
+#undef DEF
+#define DEF(type, name) \
+ { type, #name, offsetof(struct socket_settings, name) }
+
+static struct setting_def socket_setting_defs[] = {
+ DEF(SET_STR, path),
+ DEF(SET_INT, mode),
+ DEF(SET_STR, user),
+ DEF(SET_STR, group),
+
+ { 0, NULL, 0 }
+};
+
+#undef DEF
+#define DEF(type, name) \
+ { type, #name, offsetof(struct auth_socket_settings, name) }
+
+static struct setting_def auth_socket_setting_defs[] = {
+ DEF(SET_STR, type),
{ 0, NULL, 0 }
};
MEMBER(count) 1,
MEMBER(process_size) 256,
- MEMBER(extra_sockets) NULL,
/* .. */
MEMBER(uid) 0,
- MEMBER(gid) 0
+ MEMBER(gid) 0,
+ MEMBER(sockets) NULL
};
static pool_t settings_pool, settings2_pool;
return TRUE;
}
+static int settings_have_connect_sockets(struct settings *set)
+{
+ struct auth_settings *auth;
+ struct server_settings *server;
+
+ for (server = set->server; server != NULL; server = server->next) {
+ for (auth = server->auths; auth != NULL; auth = auth->next) {
+ if (auth->sockets != NULL &&
+ strcmp(auth->sockets->type, "connect") == 0)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
static int settings_verify(struct settings *set)
{
const char *dir;
PKG_RUNDIR);
}
- /* wipe out contents of login directory, if it exists */
- if (unlink_directory(set->login_dir, FALSE) < 0) {
- i_error("unlink_directory() failed for %s: %m", set->login_dir);
- return FALSE;
+ /* wipe out contents of login directory, if it exists.
+ except if we're using external authentication - then we would
+ otherwise wipe existing auth sockets */
+ if (!settings_have_connect_sockets(set)) {
+ if (unlink_directory(set->login_dir, FALSE) < 0) {
+ i_error("unlink_directory() failed for %s: %m",
+ set->login_dir);
+ return FALSE;
+ }
}
if (safe_mkdir(set->login_dir, 0750,
return auth_settings_new(server, name);
}
+static struct auth_socket_settings *
+auth_socket_settings_new(struct auth_settings *auth, const char *type)
+{
+ struct auth_socket_settings *as, **as_p;
+
+ as = p_new(settings_pool, struct auth_socket_settings, 1);
+
+ as->parent = auth;
+ as->type = str_lcase(p_strdup(settings_pool, type));
+
+ as_p = &auth->sockets;
+ while (*as_p != NULL)
+ as_p = &(*as_p)->next;
+ *as_p = as;
+
+ return as;
+}
+
+static struct auth_socket_settings *
+parse_new_auth_socket(struct auth_settings *auth, const char *name,
+ const char **errormsg)
+{
+ if (strcmp(name, "connect") != 0 && strcmp(name, "listen") != 0) {
+ *errormsg = "Unknown auth socket type";
+ return NULL;
+ }
+
+ if ((auth->sockets != NULL && strcmp(name, "connect") == 0) ||
+ (auth->sockets != NULL &&
+ strcmp(auth->sockets->type, "listen") == 0)) {
+ *errormsg = "With connect auth socket no other sockets "
+ "can be used in same auth section";
+ return NULL;
+ }
+
+ return auth_socket_settings_new(auth, name);
+}
+
static struct namespace_settings *
namespace_settings_new(struct server_settings *server, const char *type)
{
key += 5;
return parse_setting_from_defs(settings_pool, auth_setting_defs,
ctx->auth, key, value);
+ case SETTINGS_TYPE_AUTH_SOCKET:
+ return parse_setting_from_defs(settings_pool,
+ auth_socket_setting_defs,
+ ctx->auth_socket, key, value);
case SETTINGS_TYPE_NAMESPACE:
return parse_setting_from_defs(settings_pool,
namespace_setting_defs,
ctx->namespace, key, value);
+ case SETTINGS_TYPE_SOCKET:
+ return parse_setting_from_defs(settings_pool,
+ socket_setting_defs,
+ ctx->socket, key, value);
}
i_unreached();
if (type == NULL) {
/* section closing */
- if (ctx->level > 0) {
- ctx->level--;
+ if (--ctx->level > 0) {
+ ctx->type = ctx->parent_type;
ctx->protocol = MAIL_PROTOCOL_ANY;
+
+ switch (ctx->type) {
+ case SETTINGS_TYPE_AUTH_SOCKET:
+ ctx->parent_type = SETTINGS_TYPE_AUTH;
+ break;
+ default:
+ ctx->parent_type = SETTINGS_TYPE_ROOT;
+ break;
+ }
} else {
- ctx->type = ctx->parent_type;
- ctx->parent_type = SETTINGS_TYPE_ROOT;
+ ctx->type = SETTINGS_TYPE_ROOT;
ctx->server = ctx->root;
ctx->auth = &ctx->root->auth_defaults;
ctx->namespace = NULL;
return TRUE;
}
+ ctx->level++;
+ ctx->parent_type = ctx->type;
+
if (strcmp(type, "server") == 0) {
if (ctx->type != SETTINGS_TYPE_ROOT) {
*errormsg = "Server section not allowed here";
return FALSE;
}
- ctx->parent_type = ctx->type;
ctx->type = SETTINGS_TYPE_SERVER;
-
ctx->server = create_new_server(name, ctx->server->imap,
ctx->server->pop3);
server = ctx->root;
if (strcmp(type, "protocol") == 0) {
if ((ctx->type != SETTINGS_TYPE_ROOT &&
ctx->type != SETTINGS_TYPE_SERVER) ||
- ctx->level != 0) {
+ ctx->level != 1) {
*errormsg = "Protocol section not allowed here";
return FALSE;
}
*errormsg = "Unknown protocol name";
return FALSE;
}
- ctx->level++;
return TRUE;
}
return ctx->auth != NULL;
}
+ if (ctx->type == SETTINGS_TYPE_AUTH &&
+ strcmp(type, "socket") == 0) {
+ ctx->type = SETTINGS_TYPE_AUTH_SOCKET;
+ ctx->auth_socket = parse_new_auth_socket(ctx->auth,
+ name, errormsg);
+ return ctx->auth_socket != NULL;
+ }
+
+ if (ctx->type == SETTINGS_TYPE_AUTH_SOCKET) {
+ ctx->type = SETTINGS_TYPE_SOCKET;
+
+ if (strcmp(type, "master") == 0) {
+ ctx->socket = &ctx->auth_socket->master;
+ return TRUE;
+ }
+
+ if (strcmp(type, "client") == 0) {
+ ctx->socket = &ctx->auth_socket->client;
+ return TRUE;
+ }
+ }
+
if (strcmp(type, "namespace") == 0) {
if (ctx->type != SETTINGS_TYPE_ROOT &&
ctx->type != SETTINGS_TYPE_SERVER) {
int listen_fd, ssl_listen_fd;
};
+struct socket_settings {
+ const char *path;
+ unsigned int mode;
+ const char *user;
+ const char *group;
+};
+
+struct auth_socket_settings {
+ struct auth_settings *parent;
+ struct auth_socket_settings *next;
+
+ const char *type;
+ struct socket_settings master;
+ struct socket_settings client;
+};
+
struct auth_settings {
struct server_settings *parent;
struct auth_settings *next;
unsigned int count;
unsigned int process_size;
- const char *extra_sockets;
/* .. */
uid_t uid;
gid_t gid;
+ struct auth_socket_settings *sockets;
};
struct namespace_settings {