1 /*******************************************************************************
5 * Description: Implements the AF_UNIX IPC method.
7 * Copyright (c) 1997-2000 Messaging Direct Ltd.
10 * Portions Copyright (c) 2003 Jeremy Rumpf
11 * jrumpf@heavyload.net
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
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.
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
39 * This source file created using 8 space tabs.
41 ********************************************************************************/
43 /****************************************
44 * enable/disable ifdef
45 *****************************************/
46 #include "saslauthd-main.h"
49 /****************************************/
51 /****************************************
53 *****************************************/
56 #include <sys/types.h>
59 #include <sys/socket.h>
63 #include <netinet/in.h>
68 /****************************************
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();
76 /****************************************
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 */
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 *****************************************/
98 /*************************************************************
99 * IPC init. Initialize the environment specific to the
100 * AF_UNIX IPC method.
102 * __Required Function__
103 **************************************************************/
106 size_t sock_file_len;
108 /*********************************************************
109 * When we're not preforking, using an accept lock is a
110 * waste of resources. Otherwise, setup the accept lock
112 **********************************************************/
114 flags &= ~USE_ACCEPT_LOCK;
116 if (flags & USE_ACCEPT_LOCK) {
117 size_t accept_file_len;
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");
125 strlcpy(accept_file, run_path, accept_file_len);
126 strlcat(accept_file, ACCEPT_LOCK_FILE, accept_file_len);
128 if ((accept_fd = open(accept_file, O_RDWR|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR)) == -1) {
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));
136 logger(L_DEBUG, L_FUNC, "using accept lock file: %s", accept_file);
139 /**************************************************************
140 * We're at the point where we can't really do anything else
141 * until we attempt to detach or daemonize.
142 **************************************************************/
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");
154 strlcpy(sock_file, run_path, sock_file_len);
155 strlcat(sock_file, SOCKET_FILE, sock_file_len);
158 memset(&server, 0, sizeof(server));
159 strlcpy(server.sun_path, sock_file, sizeof(server.sun_path));
160 server.sun_family = AF_UNIX;
162 if ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
164 logger(L_ERR, L_FUNC, "could not create socket");
165 logger(L_ERR, L_FUNC, "socket: %s", strerror(rc));
171 if (bind(sock_fd, (struct sockaddr *)&server, sizeof(server)) == -1) {
173 logger(L_ERR, L_FUNC, "could not bind to socket: %s", sock_file);
174 logger(L_ERR, L_FUNC, "bind: %s", strerror(rc));
178 if (chmod(sock_file, S_IRWXU|S_IRWXG|S_IRWXO) == -1) {
180 logger(L_ERR, L_FUNC, "could not chmod socket: %s", sock_file);
181 logger(L_ERR, L_FUNC, "chmod: %s", strerror(rc));
185 fchmod(sock_fd, S_IRWXU|S_IRWXG|S_IRWXO);
189 if (listen(sock_fd, SOCKET_BACKLOG) == -1) {
191 logger(L_ERR, L_FUNC, "could not listen on socket: %s", sock_file);
192 logger(L_ERR, L_FUNC, "listen: %s", strerror(rc));
197 logger(L_INFO, L_FUNC, "listening on socket: %s", sock_file);
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 **************************************************************/
205 flags |= USE_PROCESS_MODEL;
210 /*************************************************************
211 * Main IPC loop. Handle all the socket accept stuff, fork if
212 * needed, then pass things off to do_request().
214 * __Required Function__
215 **************************************************************/
224 len = sizeof(client);
226 /**************************************************************
227 * First, if needed, get the accept lock. If it fails, take a
228 * nap and go to the top of the loop. (or should we just die?)
229 *************************************************************/
230 if (get_accept_lock() != 0) {
235 conn_fd = accept(sock_fd, (struct sockaddr *)&client, &len);
242 logger(L_ERR, L_FUNC, "socket accept failure");
243 logger(L_ERR, L_FUNC, "accept: %s", strerror(rc));
249 /**************************************************************
250 * If we're running one shot, drop off a kid to handle the
252 *************************************************************/
253 if (num_procs == 0) {
254 if(flags & DETACH_TTY) {
255 if (have_baby() > 0) { /* parent */
260 close(sock_fd); /* child */
266 if(flags & DETACH_TTY) {
274 /**************************************************************
275 * Normal prefork mode.
276 *************************************************************/
285 /*************************************************************
286 * General cleanup. Unlock, close, and unlink our files.
288 * __Required Function__
289 **************************************************************/
292 struct flock lock_st;
294 if (flags & USE_ACCEPT_LOCK) {
296 lock_st.l_type = F_UNLCK;
298 lock_st.l_whence = SEEK_SET;
301 fcntl(accept_fd, F_SETLK, &lock_st);
307 logger(L_DEBUG, L_FUNC, "accept lock file removed: %s", accept_file);
314 logger(L_DEBUG, L_FUNC, "socket removed: %s", sock_file);
318 /*************************************************************
319 * Handle the comms on the socket, pass the request off to
320 * do_auth() back in saslauthd-main.c, then transmit the
321 * result back out on the socket.
322 **************************************************************/
323 void do_request(int conn_fd) {
325 unsigned short count; /* input/output data byte count */
326 unsigned short ncount; /* input/output data byte count, network */
327 char *response; /* response to send to the client */
328 char login[MAX_REQ_LEN + 1]; /* account name to authenticate */
329 char password[MAX_REQ_LEN + 1]; /* password for authentication */
330 char service[MAX_REQ_LEN + 1]; /* service name for authentication */
331 char realm[MAX_REQ_LEN + 1]; /* user realm for authentication */
334 /**************************************************************
335 * The input data stream consists of the login id, password,
336 * service name and user realm as counted length strings.
337 * We read in each string, then dispatch the data.
338 **************************************************************/
341 if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count))
344 count = ntohs(count);
346 if (count > MAX_REQ_LEN) {
347 logger(L_ERR, L_FUNC, "login exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN);
348 send_no(conn_fd, "");
352 if (rx_rec(conn_fd, (void *)login, (size_t)count) != (ssize_t)count)
358 if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count))
361 count = ntohs(count);
363 if (count > MAX_REQ_LEN) {
364 logger(L_ERR, L_FUNC, "password exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN);
365 send_no(conn_fd, "");
369 if (rx_rec(conn_fd, (void *)password, (size_t)count) != (ssize_t)count)
372 password[count] = '\0';
375 if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count))
378 count = ntohs(count);
380 if (count > MAX_REQ_LEN) {
381 logger(L_ERR, L_FUNC, "service exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN);
382 send_no(conn_fd, "");
386 if (rx_rec(conn_fd, (void *)service, (size_t)count) != (ssize_t)count)
389 service[count] = '\0';
392 if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count))
395 count = ntohs(count);
397 if (count > MAX_REQ_LEN) {
398 logger(L_ERR, L_FUNC, "realm exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN);
399 send_no(conn_fd, "");
403 if (rx_rec(conn_fd, (void *)realm, (size_t)count) != (ssize_t)count)
408 /**************************************************************
409 * We don't allow NULL passwords or login names
410 **************************************************************/
411 if (*login == '\0') {
412 logger(L_ERR, L_FUNC, "NULL login received");
413 send_no(conn_fd, "NULL login received");
417 if (*password == '\0') {
418 logger(L_ERR, L_FUNC, "NULL password received");
419 send_no(conn_fd, "NULL password received");
423 /**************************************************************
424 * Get the mechanism response from do_auth() and send it back.
425 **************************************************************/
426 response = do_auth(login, password, service, realm);
428 memset(password, 0, strlen(password));
430 if (response == NULL) {
431 send_no(conn_fd, "NULL response from mechanism");
435 count = strlen(response);
436 ncount = htons(count);
438 if (tx_rec(conn_fd, (void *)&ncount, (size_t)sizeof(ncount)) != (ssize_t)sizeof(ncount)) {
443 if (tx_rec(conn_fd, (void *)response, (size_t)count) != (ssize_t)sizeof(count)) {
449 logger(L_DEBUG, L_FUNC, "response: %s", response);
457 /*************************************************************
458 * In case something went out to lunch while reading in the
459 * request data, we may want to attempt to send out a default
460 * "NO" response on the socket. The mesg is optional.
461 **************************************************************/
462 void send_no(int conn_fd, char *mesg) {
464 unsigned short count;
465 unsigned short ncount;
471 /* buff, except for the trailing NUL and 'NO ' */
472 strncpy(buff + 3, mesg, sizeof(buff) - 1 - 3);
475 count = strlen(buff);
476 ncount = htons(count);
478 if (tx_rec(conn_fd, (void *)&ncount, (size_t)sizeof(ncount)) != (ssize_t)sizeof(ncount))
481 if (tx_rec(conn_fd, (void *)buff, (size_t)count) != (ssize_t)sizeof(count))
485 logger(L_DEBUG, L_FUNC, "response: %s", buff);
491 /*************************************************************
492 * Attempt to get a write lock on the accept lock file.
493 * Return 0 if everything went ok, return -1 if something bad
494 * happened. This function is expected to block.
495 **************************************************************/
496 int get_accept_lock() {
498 struct flock lock_st;
502 if (!(flags & USE_ACCEPT_LOCK))
505 lock_st.l_type = F_WRLCK;
507 lock_st.l_whence = SEEK_SET;
513 rc = fcntl(accept_fd, F_SETLKW, &lock_st);
514 } while (rc != 0 && errno == EINTR);
518 logger(L_ERR, L_FUNC, "could not acquire accept lock");
519 logger(L_ERR, L_FUNC, "fcntl: %s", strerror(rc));
524 logger(L_DEBUG, L_FUNC, "acquired accept lock");
530 /*************************************************************
531 * Attempt to release the write lock on the accept lock file.
532 * Return 0 if everything went ok, return -1 if something bad
534 **************************************************************/
535 int rel_accept_lock() {
537 struct flock lock_st;
541 if (!(flags & USE_ACCEPT_LOCK))
544 lock_st.l_type = F_UNLCK;
546 lock_st.l_whence = SEEK_SET;
552 rc = fcntl(accept_fd, F_SETLKW, &lock_st);
553 } while (rc != 0 && errno == EINTR);
557 logger(L_ERR, L_FUNC, "could not release accept lock");
558 logger(L_ERR, L_FUNC, "fcntl: %s", strerror(rc));
563 logger(L_DEBUG, L_FUNC, "released accept lock");
570 #endif /* USE_UNIX_IPC */