0
|
1 /* Copyright (C) 2002 Timo Sirainen */
|
|
2
|
|
3 #include "common.h"
|
|
4 #include "network.h"
|
|
5 #include "iobuffer.h"
|
|
6 #include "fdpass.h"
|
|
7 #include "restrict-access.h"
|
|
8 #include "login-process.h"
|
|
9 #include "auth-process.h"
|
|
10 #include "master-interface.h"
|
|
11
|
|
12 #include <stdlib.h>
|
|
13 #include <unistd.h>
|
|
14 #include <syslog.h>
|
|
15
|
|
16 typedef struct {
|
|
17 int refcount;
|
|
18
|
|
19 pid_t pid;
|
|
20 int fd;
|
|
21 IO io;
|
|
22 IOBuffer *outbuf;
|
|
23 unsigned int destroyed:1;
|
|
24 } LoginProcess;
|
|
25
|
|
26 typedef struct {
|
|
27 LoginProcess *process;
|
|
28 int login_id;
|
|
29 int auth_id;
|
|
30 int fd;
|
|
31
|
|
32 char login_tag[LOGIN_TAG_SIZE];
|
|
33 } LoginAuthRequest;
|
|
34
|
|
35 static int auth_id_counter;
|
|
36 static Timeout to;
|
|
37 static HashTable *processes = NULL;
|
|
38
|
|
39 static void login_process_destroy(LoginProcess *p);
|
|
40 static void login_process_unref(LoginProcess *p);
|
|
41
|
|
42 static void auth_callback(AuthCookieReplyData *cookie_reply, void *user_data)
|
|
43 {
|
|
44 const char *env[] = {
|
|
45 "MAIL", NULL,
|
|
46 "LOGIN_TAG", NULL,
|
|
47 NULL
|
|
48 };
|
|
49 LoginAuthRequest *request = user_data;
|
|
50 LoginProcess *process;
|
|
51 MasterReply reply;
|
|
52
|
|
53 env[1] = cookie_reply->mail;
|
|
54 env[3] = request->login_tag;
|
|
55
|
|
56 if (cookie_reply == NULL || !cookie_reply->success)
|
|
57 reply.result = MASTER_RESULT_FAILURE;
|
|
58 else {
|
|
59 reply.result = create_imap_process(request->fd,
|
|
60 cookie_reply->user,
|
|
61 cookie_reply->uid,
|
|
62 cookie_reply->gid,
|
|
63 cookie_reply->home,
|
|
64 cookie_reply->chroot, env);
|
|
65 }
|
|
66
|
|
67 /* reply to login */
|
|
68 reply.id = request->login_id;
|
|
69
|
|
70 process = request->process;
|
|
71 if (io_buffer_send(process->outbuf, &reply, sizeof(reply)) < 0)
|
|
72 login_process_destroy(process);
|
|
73
|
|
74 (void)close(request->fd);
|
|
75 login_process_unref(process);
|
|
76 i_free(request);
|
|
77 }
|
|
78
|
|
79 static void login_process_input(void *user_data, int fd __attr_unused__,
|
|
80 IO io __attr_unused__)
|
|
81 {
|
|
82 LoginProcess *p = user_data;
|
|
83 AuthProcess *auth_process;
|
|
84 LoginAuthRequest *authreq;
|
|
85 MasterRequest req;
|
|
86 int client_fd, ret;
|
|
87
|
|
88 ret = fd_read(p->fd, &req, sizeof(req), &client_fd);
|
|
89 if (ret != sizeof(req)) {
|
|
90 if (ret == 0) {
|
|
91 /* disconnected, ie. the login process died */
|
|
92 } else if (ret > 0) {
|
|
93 /* req wasn't fully read */
|
|
94 i_error("login: fd_read() couldn't read all req");
|
|
95 } else {
|
|
96 i_error("login: fd_read() failed: %m");
|
|
97 }
|
|
98
|
|
99 login_process_destroy(p);
|
|
100 return;
|
|
101 }
|
|
102
|
|
103 /* login process isn't trusted, validate all data to make sure
|
|
104 it's not trying to exploit us */
|
|
105 if (!VALIDATE_STR(req.login_tag)) {
|
|
106 i_error("login: Received corrupted data");
|
|
107 login_process_destroy(p);
|
|
108 return;
|
|
109 }
|
|
110
|
|
111 /* ask the cookie from the auth process */
|
|
112 authreq = i_new(LoginAuthRequest, 1);
|
|
113 p->refcount++;
|
|
114 authreq->process = p;
|
|
115 authreq->login_id = req.id;
|
|
116 authreq->auth_id = ++auth_id_counter;
|
|
117 authreq->fd = client_fd;
|
|
118 strcpy(authreq->login_tag, req.login_tag);
|
|
119
|
|
120 auth_process = auth_process_find(req.auth_process);
|
|
121 if (auth_process == NULL) {
|
|
122 i_error("login: Authentication process %u doesn't exist",
|
|
123 req.auth_process);
|
|
124 auth_callback(NULL, &authreq);
|
|
125 } else {
|
|
126 auth_process_request(auth_process, authreq->auth_id, req.cookie,
|
|
127 auth_callback, authreq);
|
|
128 }
|
|
129 }
|
|
130
|
|
131 static LoginProcess *login_process_new(pid_t pid, int fd)
|
|
132 {
|
|
133 LoginProcess *p;
|
|
134
|
|
135 PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_LOGIN);
|
|
136
|
|
137 p = i_new(LoginProcess, 1);
|
|
138 p->refcount = 1;
|
|
139 p->pid = pid;
|
|
140 p->fd = fd;
|
|
141 p->io = io_add(fd, IO_READ, login_process_input, p);
|
|
142 p->outbuf = io_buffer_create(fd, default_pool, IO_PRIORITY_DEFAULT,
|
|
143 sizeof(MasterReply)*10);
|
|
144
|
|
145 hash_insert(processes, INT_TO_POINTER(pid), p);
|
|
146 return p;
|
|
147 }
|
|
148
|
|
149 static void login_process_destroy(LoginProcess *p)
|
|
150 {
|
|
151 if (p->destroyed)
|
|
152 return;
|
|
153 p->destroyed = TRUE;
|
|
154
|
|
155 io_buffer_close(p->outbuf);
|
|
156 io_remove(p->io);
|
|
157 (void)close(p->fd);
|
|
158
|
|
159 hash_remove(processes, INT_TO_POINTER(p->pid));
|
|
160 login_process_unref(p);
|
|
161 }
|
|
162
|
|
163 static void login_process_unref(LoginProcess *p)
|
|
164 {
|
|
165 if (--p->refcount > 0)
|
|
166 return;
|
|
167
|
|
168 io_buffer_destroy(p->outbuf);
|
|
169 i_free(p);
|
|
170 }
|
|
171
|
|
172 static pid_t create_login_process(void)
|
|
173 {
|
|
174 static const char *argv[] = { NULL, NULL };
|
|
175 pid_t pid;
|
|
176 int fd[2];
|
|
177
|
|
178 if (set_login_uid == 0)
|
|
179 i_fatal("Login process must not run as root");
|
|
180
|
|
181 /* create communication to process with a socket pair */
|
|
182 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1) {
|
|
183 i_error("socketpair() failed: %m");
|
|
184 return -1;
|
|
185 }
|
|
186
|
|
187 pid = fork();
|
|
188 if (pid < 0) {
|
|
189 (void)close(fd[0]);
|
|
190 (void)close(fd[1]);
|
|
191 i_error("fork() failed: %m");
|
|
192 return -1;
|
|
193 }
|
|
194
|
|
195 if (pid != 0) {
|
|
196 /* master */
|
|
197 login_process_new(pid, fd[0]);
|
|
198 (void)close(fd[1]);
|
|
199 return pid;
|
|
200 }
|
|
201
|
|
202 /* move communication handle */
|
|
203 if (dup2(fd[1], LOGIN_MASTER_SOCKET_FD) < 0)
|
|
204 i_fatal("login: dup2() failed: %m");
|
|
205
|
|
206 /* move the listen handle */
|
|
207 if (dup2(imap_fd, LOGIN_IMAP_LISTEN_FD) < 0)
|
|
208 i_fatal("login: dup2() failed: %m");
|
|
209
|
|
210 /* move the SSL listen handle */
|
|
211 if (dup2(imaps_fd, LOGIN_IMAPS_LISTEN_FD) < 0)
|
|
212 i_fatal("login: dup2() failed: %m");
|
|
213
|
|
214 /* imap_fd and imaps_fd are closed by clean_child_process() */
|
|
215
|
|
216 (void)close(fd[0]);
|
|
217 (void)close(fd[1]);
|
|
218
|
|
219 clean_child_process();
|
|
220
|
|
221 /* setup access environment - needs to be done after
|
|
222 clean_child_process() since it clears environment */
|
|
223 restrict_access_set_env(set_login_user, set_login_uid, set_login_gid,
|
|
224 set_login_chroot ? set_login_dir : NULL);
|
|
225
|
|
226 if (!set_login_chroot) {
|
|
227 /* no chrooting, but still change to the directory */
|
|
228 if (chdir(set_login_dir) < 0) {
|
|
229 i_fatal("chdir(%s) failed: %m",
|
|
230 set_login_dir);
|
|
231 }
|
|
232 }
|
|
233
|
|
234 if (set_ssl_cert_file != NULL) {
|
|
235 putenv((char *) t_strconcat("SSL_CERT_FILE=",
|
|
236 set_ssl_cert_file, NULL));
|
|
237 }
|
|
238
|
|
239 if (set_ssl_key_file != NULL) {
|
|
240 putenv((char *) t_strconcat("SSL_KEY_FILE=",
|
|
241 set_ssl_key_file, NULL));
|
|
242 }
|
|
243
|
|
244 if (set_disable_plaintext_auth)
|
|
245 putenv("DISABLE_PLAINTEXT_AUTH=1");
|
|
246
|
|
247 putenv((char *) t_strdup_printf("MAX_LOGGING_USERS=%d",
|
|
248 set_max_logging_users));
|
|
249
|
|
250 /* hide the path, it's ugly */
|
|
251 argv[0] = strrchr(set_login_executable, '/');
|
|
252 if (argv[0] == NULL) argv[0] = set_login_executable; else argv[0]++;
|
|
253
|
|
254 execv(set_login_executable, (char **) argv);
|
|
255
|
|
256 i_fatal("execv(%s) failed: %m", argv[0]);
|
|
257 return -1;
|
|
258 }
|
|
259
|
|
260 static void login_hash_cleanup(void *key __attr_unused__, void *value,
|
|
261 void *user_data __attr_unused__)
|
|
262 {
|
|
263 LoginProcess *p = value;
|
|
264
|
|
265 (void)close(p->fd);
|
|
266 }
|
|
267
|
|
268 void login_processes_cleanup(void)
|
|
269 {
|
|
270 hash_foreach(processes, login_hash_cleanup, NULL);
|
|
271 }
|
|
272
|
|
273 static void login_processes_start_missing(void *user_data __attr_unused__,
|
|
274 Timeout timeout __attr_unused__)
|
|
275 {
|
|
276 /* create max. one process every second, that way if it keeps
|
|
277 dying all the time we don't eat all cpu with fork()ing. */
|
|
278 if (hash_size(processes) < set_login_processes_count)
|
|
279 (void)create_login_process();
|
|
280 }
|
|
281
|
|
282 void login_processes_init(void)
|
|
283 {
|
|
284 auth_id_counter = 0;
|
|
285 processes = hash_create(default_pool, 128, NULL, NULL);
|
|
286 to = timeout_add(1000, login_processes_start_missing, NULL);
|
|
287 }
|
|
288
|
|
289 static void login_hash_destroy(void *key __attr_unused__, void *value,
|
|
290 void *user_data __attr_unused__)
|
|
291 {
|
|
292 login_process_destroy(value);
|
|
293 }
|
|
294
|
|
295 void login_processes_deinit(void)
|
|
296 {
|
|
297 timeout_remove(to);
|
|
298
|
|
299 hash_foreach(processes, login_hash_destroy, NULL);
|
|
300 hash_destroy(processes);
|
|
301 }
|