Mercurial > dovecot > original-hg > dovecot-1.2
view src/master/auth-process.c @ 953:411006be3c66 HEAD
Naming change for function typedefs.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sat, 11 Jan 2003 21:55:56 +0200 |
parents | 2e649dec0f09 |
children | d0845dca7eca |
line wrap: on
line source
/* Copyright (C) 2002 Timo Sirainen */ #include "common.h" #include "ioloop.h" #include "env-util.h" #include "fd-close-on-exec.h" #include "network.h" #include "ostream.h" #include "restrict-access.h" #include "restrict-process-size.h" #include "auth-process.h" #include <stdlib.h> #include <unistd.h> #include <pwd.h> #include <syslog.h> #include <sys/stat.h> struct auth_process { struct auth_process *next; char *name; pid_t pid; int fd; struct io *io; struct ostream *output; unsigned int reply_pos; char reply_buf[sizeof(struct auth_cookie_reply_data)]; struct waiting_request *requests, **next_request; unsigned int initialized:1; }; struct waiting_request { struct waiting_request *next; unsigned int id; auth_callback_t callback; void *context; }; static struct timeout *to; static struct auth_process *processes; static void auth_process_destroy(struct auth_process *p); static void push_request(struct auth_process *process, unsigned int id, auth_callback_t callback, void *context) { struct waiting_request *req; req = i_new(struct waiting_request, 1); req->id = id; req->callback = callback; req->context = context; *process->next_request = req; process->next_request = &req->next; } static void pop_request(struct auth_process *process, struct auth_cookie_reply_data *reply) { struct waiting_request *req; req = process->requests; if (req == NULL) { i_warning("imap-auth %s sent us unrequested reply for id %d", dec2str(process->pid), reply->id); return; } if (reply->id != req->id) { i_fatal("imap-auth %s sent invalid id for reply " "(got %d, expecting %d)", dec2str(process->pid), reply->id, req->id); } /* auth process isn't trusted, validate all data to make sure it's not trying to exploit us */ if (!VALIDATE_STR(reply->system_user) || !VALIDATE_STR(reply->virtual_user) || !VALIDATE_STR(reply->mail) || !VALIDATE_STR(reply->home)) { i_error("auth: Received corrupted data"); auth_process_destroy(process); return; } process->requests = req->next; if (process->requests == NULL) process->next_request = &process->requests; req->callback(reply, req->context); i_free(req); } static void auth_process_input(void *context, int fd, struct io *io __attr_unused__) { struct auth_process *p = context; int ret; ret = net_receive(fd, p->reply_buf + p->reply_pos, sizeof(p->reply_buf) - p->reply_pos); if (ret < 0) { /* disconnected */ auth_process_destroy(p); return; } if (!p->initialized) { if (p->reply_buf[0] != 'O') { i_fatal("Auth process sent invalid initialization " "notification"); } p->initialized = TRUE; ret--; memmove(p->reply_buf, p->reply_buf + 1, ret); } p->reply_pos += ret; if (p->reply_pos < sizeof(p->reply_buf)) return; /* reply is now read */ pop_request(p, (struct auth_cookie_reply_data *) p->reply_buf); p->reply_pos = 0; } static struct auth_process * auth_process_new(pid_t pid, int fd, const char *name) { struct auth_process *p; PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_AUTH); p = i_new(struct auth_process, 1); p->name = i_strdup(name); p->pid = pid; p->fd = fd; p->io = io_add(fd, IO_READ, auth_process_input, p); p->output = o_stream_create_file(fd, default_pool, sizeof(struct auth_cookie_request_data)*100, IO_PRIORITY_DEFAULT, FALSE); p->next_request = &p->requests; p->next = processes; processes = p; return p; } static void auth_process_destroy(struct auth_process *p) { struct auth_process **pos; struct waiting_request *next; if (!p->initialized) i_fatal("Auth process died too early - shutting down"); for (pos = &processes; *pos != NULL; pos = &(*pos)->next) { if (*pos == p) { *pos = p->next; break; } } for (; p->requests != NULL; p->requests = next) { next = p->requests->next; p->requests->callback(NULL, p->requests->context); i_free(p->requests); } (void)unlink(t_strconcat(set_login_dir, "/", p->name, NULL)); o_stream_unref(p->output); io_remove(p->io); if (close(p->fd) < 0) i_error("close(auth) failed: %m"); i_free(p->name); i_free(p); } static pid_t create_auth_process(struct auth_config *config) { static char *argv[] = { NULL, NULL }; const char *path; struct passwd *pwd; pid_t pid; int fd[2], listen_fd, i; if ((pwd = getpwnam(config->user)) == NULL) i_fatal("Auth user doesn't exist: %s", config->user); /* create communication to process with a socket pair */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1) { i_error("socketpair() failed: %m"); return -1; } pid = fork(); if (pid < 0) { (void)close(fd[0]); (void)close(fd[1]); i_error("fork() failed: %m"); return -1; } if (pid != 0) { /* master */ fd_close_on_exec(fd[0], TRUE); auth_process_new(pid, fd[0], config->name); (void)close(fd[1]); return pid; } /* create socket for listening auth requests from imap-login */ path = t_strconcat(set_login_dir, "/", config->name, NULL); (void)unlink(path); (void)umask(0177); /* we want 0600 mode for the socket */ listen_fd = net_listen_unix(path); if (listen_fd < 0) i_fatal("Can't listen in UNIX socket %s: %m", path); i_assert(listen_fd > 2); /* set correct permissions */ if (chown(path, set_login_uid, set_login_gid) < 0) { i_fatal("login: chown(%s, %s, %s) failed: %m", path, dec2str(set_login_uid), dec2str(set_login_gid)); } /* move master communication handle to 0 */ if (dup2(fd[1], 0) < 0) i_fatal("login: dup2(0) failed: %m"); (void)close(fd[0]); (void)close(fd[1]); /* set /dev/null handle into 1 and 2, so if something is printed into stdout/stderr it can't go anywhere where it could cause harm */ if (dup2(null_fd, 1) < 0) i_fatal("login: dup2(1) failed: %m"); if (dup2(null_fd, 2) < 0) i_fatal("login: dup2(2) failed: %m"); clean_child_process(); /* move login communication handle to 3. do it last so we can be sure it's not closed afterwards. */ if (listen_fd != 3) { if (dup2(listen_fd, 3) < 0) i_fatal("login: dup2() failed: %m"); (void)close(listen_fd); } for (i = 0; i <= 2; i++) fd_close_on_exec(i, FALSE); /* setup access environment - needs to be done after clean_child_process() since it clears environment */ restrict_access_set_env(config->user, pwd->pw_uid, pwd->pw_gid, config->chroot); /* set other environment */ env_put(t_strconcat("AUTH_PROCESS=", dec2str(getpid()), NULL)); env_put(t_strconcat("MECHANISMS=", config->mechanisms, NULL)); env_put(t_strconcat("REALMS=", config->realms, NULL)); env_put(t_strconcat("USERINFO=", config->userinfo, NULL)); env_put(t_strconcat("USERINFO_ARGS=", config->userinfo_args, NULL)); if (config->use_cyrus_sasl) env_put("USE_CYRUS_SASL=1"); restrict_process_size(config->process_size); /* make sure we don't leak syslog fd, but do it last so that any errors above will be logged */ closelog(); /* hide the path, it's ugly */ argv[0] = strrchr(config->executable, '/'); if (argv[0] == NULL) argv[0] = config->executable; else argv[0]++; execv(config->executable, (char **) argv); i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", argv[0]); return -1; } struct auth_process *auth_process_find(unsigned int id) { struct auth_process *p; for (p = processes; p != NULL; p = p->next) { if ((unsigned int)p->pid == id) return p; } return NULL; } void auth_process_request(unsigned int login_pid, struct auth_process *process, unsigned int id, unsigned char cookie[AUTH_COOKIE_SIZE], auth_callback_t callback, void *context) { struct auth_cookie_request_data req; req.id = id; req.login_pid = login_pid; memcpy(req.cookie, cookie, AUTH_COOKIE_SIZE); if (o_stream_send(process->output, &req, sizeof(req)) < 0) auth_process_destroy(process); push_request(process, id, callback, context); } static unsigned int auth_process_get_count(const char *name) { struct auth_process *p; unsigned int count = 0; for (p = processes; p != NULL; p = p->next) { if (strcmp(p->name, name) == 0) count++; } return count; } void auth_processes_destroy_all(void) { struct auth_process *next; while (processes != NULL) { next = processes->next; auth_process_destroy(processes); processes = next; } } static void auth_processes_start_missing(void *context __attr_unused__, struct timeout *timeout __attr_unused__) { struct auth_config *config; unsigned int count; config = auth_processes_config; for (; config != NULL; config = config->next) { count = auth_process_get_count(config->name); for (; count < config->count; count++) (void)create_auth_process(config); } } void auth_processes_init(void) { processes = NULL; to = timeout_add(1000, auth_processes_start_missing, NULL); } void auth_processes_deinit(void) { timeout_remove(to); auth_processes_destroy_all(); }