1 /*******************************************************************************
2 *
3 * ipc_unix.c
4 *
5 * Description: Implements the AF_UNIX IPC method.
6 *
7 * Copyright (c) 1997-2000 Messaging Direct Ltd.
8 * All rights reserved.
9 *
10 * Portions Copyright (c) 2003 Jeremy Rumpf
11 * jrumpf@heavyload.net
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 *
22 * THIS SOFTWARE IS PROVIDED BY MESSAGING DIRECT LTD. ``AS IS'' AND ANY
23 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MESSAGING DIRECT LTD. OR
26 * ITS EMPLOYEES OR AGENTS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
29 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
31 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
32 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
33 * DAMAGE.
34 *
35 *
36 * HISTORY
37 *
38 *
39 * This source file created using 8 space tabs.
40 *
41 ********************************************************************************/
42
43 /****************************************
44 * enable/disable ifdef
45 *****************************************/
46 #include "saslauthd-main.h"
47
48 #ifdef USE_UNIX_IPC
49 /****************************************/
50
51 /****************************************
52 * includes
53 *****************************************/
54 #include <stdlib.h>
55 #include <unistd.h>
56 #include <sys/types.h>
57 #include <sys/stat.h>
58 #include <fcntl.h>
59 #include <sys/socket.h>
60 #include <sys/un.h>
61 #include <string.h>
62 #include <errno.h>
63 #include <netinet/in.h>
64
65 #include "globals.h"
66 #include "utils.h"
67
68 /****************************************
69 * declarations/protos
70 *****************************************/
71 static void do_request(int);
72 static void send_no(int, char *);
73 static int rel_accept_lock();
74 static int get_accept_lock();
75
76 /****************************************
77 * module globals
78 *****************************************/
79 static int sock_fd; /* descriptor for the socket */
80 static int accept_fd; /* descriptor for the accept lock */
81 static struct sockaddr_un server; /* domain socket control, server side */
82 static struct sockaddr_un client; /* domain socket control, client side */
83 static SALEN_TYPE len; /* length for the client sockaddr_un */
84 static char *sock_file; /* path to the AF_UNIX socket */
85 static char *accept_file;/* path to the accept() lock file */
86
87 /****************************************
88 * flags global from saslauthd-main.c
89 * run_path global from saslauthd-main.c
90 * num_procs global from saslauthd-main.c
91 * detach_tty() function from saslauthd-main.c
92 * rx_rec() function from utils.c
93 * tx_rec() function from utils.c
94 * logger() function from utils.c
95 *****************************************/
96
97
98 /*************************************************************
99 * IPC init. Initialize the environment specific to the
100 * AF_UNIX IPC method.
101 *
102 * __Required Function__
103 **************************************************************/
ipc_init()104 void ipc_init() {
105 int rc;
106 size_t sock_file_len;
107
108 /*********************************************************
109 * When we're not preforking, using an accept lock is a
110 * waste of resources. Otherwise, setup the accept lock
111 * file.
112 **********************************************************/
113 if (num_procs == 0)
114 flags &= ~USE_ACCEPT_LOCK;
115
116 if (flags & USE_ACCEPT_LOCK) {
117 size_t accept_file_len;
118
119 accept_file_len = strlen(run_path) + sizeof(ACCEPT_LOCK_FILE) + 1;
120 if ((accept_file = malloc(accept_file_len)) == NULL) {
121 logger(L_ERR, L_FUNC, "could not allocate memory");
122 exit(1);
123 }
124
125 strlcpy(accept_file, run_path, accept_file_len);
126 strlcat(accept_file, ACCEPT_LOCK_FILE, accept_file_len);
127
128 if ((accept_fd = open(accept_file, O_RDWR|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR)) == -1) {
129 rc = errno;
130 logger(L_ERR, L_FUNC, "could not open accept lock file: %s", accept_file);
131 logger(L_ERR, L_FUNC, "open: %s", strerror(rc));
132 exit(1);
133 }
134
135 if (flags & VERBOSE)
136 logger(L_DEBUG, L_FUNC, "using accept lock file: %s", accept_file);
137 }
138
139 /**************************************************************
140 * We're at the point where we can't really do anything else
141 * until we attempt to detach or daemonize.
142 **************************************************************/
143 detach_tty();
144
145 /**************************************************************
146 * Setup the UNIX domain socket
147 **************************************************************/
148 sock_file_len = strlen(run_path) + sizeof(SOCKET_FILE) + 1;
149 if ((sock_file = malloc(sock_file_len)) == NULL) {
150 logger(L_ERR, L_FUNC, "could not allocate memory");
151 exit(1);
152 }
153
154 strlcpy(sock_file, run_path, sock_file_len);
155 strlcat(sock_file, SOCKET_FILE, sock_file_len);
156
157 unlink(sock_file);
158 memset(&server, 0, sizeof(server));
159 strlcpy(server.sun_path, sock_file, sizeof(server.sun_path));
160 server.sun_family = AF_UNIX;
161
162 if ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
163 rc = errno;
164 logger(L_ERR, L_FUNC, "could not create socket");
165 logger(L_ERR, L_FUNC, "socket: %s", strerror(rc));
166 exit(1);
167 }
168
169 umask(0);
170
171 if (bind(sock_fd, (struct sockaddr *)&server, sizeof(server)) == -1) {
172 rc = errno;
173 logger(L_ERR, L_FUNC, "could not bind to socket: %s", sock_file);
174 logger(L_ERR, L_FUNC, "bind: %s", strerror(rc));
175 exit(1);
176 }
177
178 if (chmod(sock_file, S_IRWXU|S_IRWXG|S_IRWXO) == -1) {
179 rc = errno;
180 logger(L_ERR, L_FUNC, "could not chmod socket: %s", sock_file);
181 logger(L_ERR, L_FUNC, "chmod: %s", strerror(rc));
182 exit(1);
183 }
184
185 fchmod(sock_fd, S_IRWXU|S_IRWXG|S_IRWXO);
186
187 umask(077);
188
189 if (listen(sock_fd, SOCKET_BACKLOG) == -1) {
190 rc = errno;
191 logger(L_ERR, L_FUNC, "could not listen on socket: %s", sock_file);
192 logger(L_ERR, L_FUNC, "listen: %s", strerror(rc));
193 exit(1);
194 }
195
196
197 logger(L_INFO, L_FUNC, "listening on socket: %s", sock_file);
198
199 /**************************************************************
200 * Ok boys... Let's procreate... If necessary of course...
201 * Num_procs == 0 means we're running one shot per process. In
202 * that case, we'll handle forking on a per connection basis.
203 **************************************************************/
204 if (num_procs != 0)
205 flags |= USE_PROCESS_MODEL;
206
207 return;
208 }
209
210 /*************************************************************
211 * Main IPC loop. Handle all the socket accept stuff, fork if
212 * needed, then pass things off to do_request().
213 *
214 * __Required Function__
215 **************************************************************/
ipc_loop()216 void ipc_loop() {
217
218 int rc;
219 int conn_fd;
220 unsigned char dummy;
221
222
223 while(1) {
224
225 len = sizeof(client);
226
227 /**************************************************************
228 * First, if needed, get the accept lock. If it fails, take a
229 * nap and go to the top of the loop. (or should we just die?)
230 *************************************************************/
231 if (get_accept_lock() != 0) {
232 sleep(5);
233 continue;
234 }
235
236 conn_fd = accept(sock_fd, (struct sockaddr *)&client, (unsigned int *) &len);
237 rc = errno;
238
239 rel_accept_lock();
240
241 if (conn_fd == -1) {
242 if (rc != EINTR) {
243 logger(L_ERR, L_FUNC, "socket accept failure");
244 logger(L_ERR, L_FUNC, "accept: %s", strerror(rc));
245 sleep(5);
246 }
247 continue;
248 }
249
250 /**************************************************************
251 * If we're running one shot, drop off a kid to handle the
252 * connection.
253 *************************************************************/
254 if (num_procs == 0) {
255 if(flags & DETACH_TTY) {
256 if (have_baby() > 0) { /* parent */
257 close(conn_fd);
258 continue;
259 }
260
261 close(sock_fd); /* child */
262 }
263
264 do_request(conn_fd);
265 shutdown(conn_fd, SHUT_WR);
266 while (read(conn_fd, &dummy, 1) > 0) { }
267 close(conn_fd);
268
269 if(flags & DETACH_TTY) {
270 exit(0);
271 } else {
272 continue;
273 }
274
275 }
276
277 /**************************************************************
278 * Normal prefork mode.
279 *************************************************************/
280 do_request(conn_fd);
281 shutdown(conn_fd, SHUT_WR);
282 while (read(conn_fd, &dummy, 1) > 0) { }
283 close(conn_fd);
284 }
285
286 return;
287 }
288
289
290 /*************************************************************
291 * General cleanup. Unlock, close, and unlink our files.
292 *
293 * __Required Function__
294 **************************************************************/
ipc_cleanup()295 void ipc_cleanup() {
296
297 struct flock lock_st;
298
299 if (flags & USE_ACCEPT_LOCK) {
300
301 lock_st.l_type = F_UNLCK;
302 lock_st.l_start = 0;
303 lock_st.l_whence = SEEK_SET;
304 lock_st.l_len = 1;
305
306 fcntl(accept_fd, F_SETLK, &lock_st);
307
308 close(accept_fd);
309 unlink(accept_file);
310
311 if (flags & VERBOSE)
312 logger(L_DEBUG, L_FUNC, "accept lock file removed: %s", accept_file);
313 }
314
315 close(sock_fd);
316 unlink(sock_file);
317
318 if (flags & VERBOSE)
319 logger(L_DEBUG, L_FUNC, "socket removed: %s", sock_file);
320 }
321
322
323 /*************************************************************
324 * Handle the comms on the socket, pass the request off to
325 * do_auth() back in saslauthd-main.c, then transmit the
326 * result back out on the socket.
327 **************************************************************/
do_request(int conn_fd)328 void do_request(int conn_fd) {
329
330 unsigned short count; /* input/output data byte count */
331 unsigned short ncount; /* input/output data byte count, network */
332 char *response; /* response to send to the client */
333 char login[MAX_REQ_LEN + 1]; /* account name to authenticate */
334 char password[MAX_REQ_LEN + 1]; /* password for authentication */
335 char service[MAX_REQ_LEN + 1]; /* service name for authentication */
336 char realm[MAX_REQ_LEN + 1]; /* user realm for authentication */
337
338
339 /**************************************************************
340 * The input data stream consists of the login id, password,
341 * service name and user realm as counted length strings.
342 * We read in each string, then dispatch the data.
343 **************************************************************/
344
345 /* login id */
346 if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count))
347 return;
348
349 count = ntohs(count);
350
351 if (count > MAX_REQ_LEN) {
352 logger(L_ERR, L_FUNC, "login exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN);
353 send_no(conn_fd, "");
354 return;
355 }
356
357 if (rx_rec(conn_fd, (void *)login, (size_t)count) != (ssize_t)count)
358 return;
359
360 login[count] = '\0';
361
362 /* password */
363 if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count))
364 return;
365
366 count = ntohs(count);
367
368 if (count > MAX_REQ_LEN) {
369 logger(L_ERR, L_FUNC, "password exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN);
370 send_no(conn_fd, "");
371 return;
372 }
373
374 if (rx_rec(conn_fd, (void *)password, (size_t)count) != (ssize_t)count)
375 return;
376
377 password[count] = '\0';
378
379 /* service */
380 if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count))
381 return;
382
383 count = ntohs(count);
384
385 if (count > MAX_REQ_LEN) {
386 logger(L_ERR, L_FUNC, "service exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN);
387 send_no(conn_fd, "");
388 return;
389 }
390
391 if (rx_rec(conn_fd, (void *)service, (size_t)count) != (ssize_t)count)
392 return;
393
394 service[count] = '\0';
395
396 /* realm */
397 if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count))
398 return;
399
400 count = ntohs(count);
401
402 if (count > MAX_REQ_LEN) {
403 logger(L_ERR, L_FUNC, "realm exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN);
404 send_no(conn_fd, "");
405 return;
406 }
407
408 if (rx_rec(conn_fd, (void *)realm, (size_t)count) != (ssize_t)count)
409 return;
410
411 realm[count] = '\0';
412
413 /**************************************************************
414 * We don't allow NULL passwords or login names
415 **************************************************************/
416 if (*login == '\0') {
417 logger(L_ERR, L_FUNC, "NULL login received");
418 send_no(conn_fd, "NULL login received");
419 return;
420 }
421
422 if (*password == '\0') {
423 logger(L_ERR, L_FUNC, "NULL password received");
424 send_no(conn_fd, "NULL password received");
425 return;
426 }
427
428 /**************************************************************
429 * Get the mechanism response from do_auth() and send it back.
430 **************************************************************/
431 response = do_auth(login, password, service, realm);
432
433 memset(password, 0, strlen(password));
434
435 if (response == NULL) {
436 send_no(conn_fd, "NULL response from mechanism");
437 return;
438 }
439
440 count = strlen(response);
441 ncount = htons(count);
442
443 if (tx_rec(conn_fd, (void *)&ncount, (size_t)sizeof(ncount)) != (ssize_t)sizeof(ncount)) {
444 free(response);
445 return;
446 }
447
448 if (tx_rec(conn_fd, (void *)response, (size_t)count) != (ssize_t)sizeof(count)) {
449 free(response);
450 return;
451 }
452
453 if (flags & VERBOSE)
454 logger(L_DEBUG, L_FUNC, "response: %s", response);
455
456 free(response);
457
458 return;
459 }
460
461
462 /*************************************************************
463 * In case something went out to lunch while reading in the
464 * request data, we may want to attempt to send out a default
465 * "NO" response on the socket. The mesg is optional.
466 **************************************************************/
send_no(int conn_fd,char * mesg)467 void send_no(int conn_fd, char *mesg) {
468 char buff[1024];
469 unsigned short count;
470 unsigned short ncount;
471
472 buff[0] = 'N';
473 buff[1] = 'O';
474 buff[2] = ' ';
475
476 /* buff, except for the trailing NUL and 'NO ' */
477 strncpy(buff + 3, mesg, sizeof(buff) - 1 - 3);
478 buff[1023] = '\0';
479
480 count = strlen(buff);
481 ncount = htons(count);
482
483 if (tx_rec(conn_fd, (void *)&ncount, (size_t)sizeof(ncount)) != (ssize_t)sizeof(ncount))
484 return;
485
486 if (tx_rec(conn_fd, (void *)buff, (size_t)count) != (ssize_t)sizeof(count))
487 return;
488
489 if (flags & VERBOSE)
490 logger(L_DEBUG, L_FUNC, "response: %s", buff);
491
492 return;
493 }
494
495
496 /*************************************************************
497 * Attempt to get a write lock on the accept lock file.
498 * Return 0 if everything went ok, return -1 if something bad
499 * happened. This function is expected to block.
500 **************************************************************/
get_accept_lock()501 int get_accept_lock() {
502
503 struct flock lock_st;
504 int rc;
505
506
507 if (!(flags & USE_ACCEPT_LOCK))
508 return 0;
509
510 lock_st.l_type = F_WRLCK;
511 lock_st.l_start = 0;
512 lock_st.l_whence = SEEK_SET;
513 lock_st.l_len = 1;
514
515 errno = 0;
516
517 do {
518 rc = fcntl(accept_fd, F_SETLKW, &lock_st);
519 } while (rc != 0 && errno == EINTR);
520
521 if (rc != 0) {
522 rc = errno;
523 logger(L_ERR, L_FUNC, "could not acquire accept lock");
524 logger(L_ERR, L_FUNC, "fcntl: %s", strerror(rc));
525 return -1;
526 }
527
528 if (flags & VERBOSE)
529 logger(L_DEBUG, L_FUNC, "acquired accept lock");
530
531 return 0;
532 }
533
534
535 /*************************************************************
536 * Attempt to release the write lock on the accept lock file.
537 * Return 0 if everything went ok, return -1 if something bad
538 * happened.
539 **************************************************************/
rel_accept_lock()540 int rel_accept_lock() {
541
542 struct flock lock_st;
543 int rc;
544
545
546 if (!(flags & USE_ACCEPT_LOCK))
547 return 0;
548
549 lock_st.l_type = F_UNLCK;
550 lock_st.l_start = 0;
551 lock_st.l_whence = SEEK_SET;
552 lock_st.l_len = 1;
553
554 errno = 0;
555
556 do {
557 rc = fcntl(accept_fd, F_SETLKW, &lock_st);
558 } while (rc != 0 && errno == EINTR);
559
560 if (rc != 0) {
561 rc = errno;
562 logger(L_ERR, L_FUNC, "could not release accept lock");
563 logger(L_ERR, L_FUNC, "fcntl: %s", strerror(rc));
564 return -1;
565 }
566
567 if (flags & VERBOSE)
568 logger(L_DEBUG, L_FUNC, "released accept lock");
569
570 return 0;
571 }
572
573
574
575 #endif /* USE_UNIX_IPC */
576