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