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 *****************************************/
71static void	do_request(int);
72static void	send_no(int, char *);
73static int	rel_accept_lock();
74static int	get_accept_lock();
75
76/****************************************
77 * module globals
78 *****************************************/
79static int			sock_fd;     /* descriptor for the socket          */
80static int			accept_fd;   /* descriptor for the accept lock     */
81static struct sockaddr_un	server;      /* domain socket control, server side */
82static struct sockaddr_un	client;      /* domain socket control, client side */
83static SALEN_TYPE		len;         /* length for the client sockaddr_un  */
84static char			*sock_file;  /* path to the AF_UNIX socket         */
85static 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 **************************************************************/
104void 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 **************************************************************/
216void ipc_loop() {
217
218	int		rc;
219	int		conn_fd;
220
221
222	while(1) {
223
224		len = sizeof(client);
225
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) {
231			sleep(5);
232			continue;
233		}
234
235        	conn_fd = accept(sock_fd, (struct sockaddr *)&client, (socklen_t *)&len); /* APPLE: cast */
236		rc = errno;
237
238		rel_accept_lock();
239
240		if (conn_fd == -1) {
241			if (rc != EINTR) {
242				logger(L_ERR, L_FUNC, "socket accept failure");
243				logger(L_ERR, L_FUNC, "accept: %s", strerror(rc));
244				sleep(5);
245			}
246			continue;
247		}
248
249		/**************************************************************
250		 * If we're running one shot, drop off a kid to handle the
251		 * connection.
252		 *************************************************************/
253		if (num_procs == 0) {
254		    if(flags & DETACH_TTY) {
255			if (have_baby() > 0) {	/* parent */
256			    close(conn_fd);
257			    continue;
258			}
259
260			close(sock_fd);         /* child  */
261		    }
262
263		    do_request(conn_fd);
264		    close(conn_fd);
265
266		    if(flags & DETACH_TTY) {
267			exit(0);
268		    } else {
269			continue;
270		    }
271
272		}
273
274		/**************************************************************
275		 * Normal prefork mode.
276		 *************************************************************/
277		do_request(conn_fd);
278		close(conn_fd);
279	}
280
281	return;
282}
283
284
285/*************************************************************
286 * General cleanup. Unlock, close, and unlink our files.
287 *
288 * __Required Function__
289 **************************************************************/
290void ipc_cleanup() {
291
292	struct flock    lock_st;
293
294	if (flags & USE_ACCEPT_LOCK) {
295
296		lock_st.l_type = F_UNLCK;
297		lock_st.l_start = 0;
298		lock_st.l_whence = SEEK_SET;
299		lock_st.l_len = 1;
300
301		fcntl(accept_fd, F_SETLK, &lock_st);
302
303		close(accept_fd);
304		unlink(accept_file);
305
306		if (flags & VERBOSE)
307			logger(L_DEBUG, L_FUNC, "accept lock file removed: %s", accept_file);
308	}
309
310	close(sock_fd);
311	unlink(sock_file);
312
313	if (flags & VERBOSE)
314		logger(L_DEBUG, L_FUNC, "socket removed: %s", sock_file);
315}
316
317
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 **************************************************************/
323void do_request(int conn_fd) {
324
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          */
332
333
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	 **************************************************************/
339
340	/* login id */
341	if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count))
342		return;
343
344	count = ntohs(count);
345
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, "");
349		return;
350	}
351
352	if (rx_rec(conn_fd, (void *)login, (size_t)count) != (ssize_t)count)
353		return;
354
355	login[count] = '\0';
356
357	/* password */
358	if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count))
359		return;
360
361	count = ntohs(count);
362
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, "");
366		return;
367	}
368
369	if (rx_rec(conn_fd, (void *)password, (size_t)count) != (ssize_t)count)
370		return;
371
372	password[count] = '\0';
373
374	/* service */
375	if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count))
376		return;
377
378	count = ntohs(count);
379
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, "");
383		return;
384	}
385
386	if (rx_rec(conn_fd, (void *)service, (size_t)count) != (ssize_t)count)
387		return;
388
389	service[count] = '\0';
390
391	/* realm */
392	if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count))
393		return;
394
395	count = ntohs(count);
396
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, "");
400		return;
401	}
402
403	if (rx_rec(conn_fd, (void *)realm, (size_t)count) != (ssize_t)count)
404		return;
405
406	realm[count] = '\0';
407
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");
414		return;
415	}
416
417	if (*password == '\0') {
418		logger(L_ERR, L_FUNC, "NULL password received");
419		send_no(conn_fd, "NULL password received");
420		return;
421	}
422
423	/**************************************************************
424	 * Get the mechanism response from do_auth() and send it back.
425	 **************************************************************/
426	response = do_auth(login, password, service, realm);
427
428	memset(password, 0, strlen(password));
429
430	if (response == NULL) {
431		send_no(conn_fd, "NULL response from mechanism");
432		return;
433	}
434
435	count = strlen(response);
436	ncount = htons(count);
437
438	if (tx_rec(conn_fd, (void *)&ncount, (size_t)sizeof(ncount)) != (ssize_t)sizeof(ncount)) {
439		free(response);
440		return;
441	}
442
443	if (tx_rec(conn_fd, (void *)response, (size_t)count) != (ssize_t)sizeof(count)) {
444		free(response);
445		return;
446	}
447
448	if (flags & VERBOSE)
449		logger(L_DEBUG, L_FUNC, "response: %s", response);
450
451	free(response);
452
453	return;
454}
455
456
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 **************************************************************/
462void send_no(int conn_fd, char *mesg) {
463	char		buff[1024];
464	unsigned short	count;
465	unsigned short	ncount;
466
467	buff[0] = 'N';
468	buff[1] = 'O';
469	buff[2] = ' ';
470
471	/* buff, except for the trailing NUL and 'NO ' */
472	strncpy(buff + 3, mesg, sizeof(buff) - 1 - 3);
473	buff[1023] = '\0';
474
475	count = strlen(buff);
476	ncount = htons(count);
477
478	if (tx_rec(conn_fd, (void *)&ncount, (size_t)sizeof(ncount)) != (ssize_t)sizeof(ncount))
479		return;
480
481	if (tx_rec(conn_fd, (void *)buff, (size_t)count) != (ssize_t)sizeof(count))
482		return;
483
484	if (flags & VERBOSE)
485		logger(L_DEBUG, L_FUNC, "response: %s", buff);
486
487	return;
488}
489
490
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 **************************************************************/
496int get_accept_lock() {
497
498	struct flock    lock_st;
499	int             rc;
500
501
502	if (!(flags & USE_ACCEPT_LOCK))
503		return 0;
504
505	lock_st.l_type = F_WRLCK;
506	lock_st.l_start = 0;
507	lock_st.l_whence = SEEK_SET;
508	lock_st.l_len = 1;
509
510	errno = 0;
511
512	do {
513		rc = fcntl(accept_fd, F_SETLKW, &lock_st);
514	} while (rc != 0 && errno == EINTR);
515
516	if (rc != 0) {
517		rc = errno;
518		logger(L_ERR, L_FUNC, "could not acquire accept lock");
519		logger(L_ERR, L_FUNC, "fcntl: %s", strerror(rc));
520		return -1;
521	}
522
523	if (flags & VERBOSE)
524		logger(L_DEBUG, L_FUNC, "acquired accept lock");
525
526	return 0;
527}
528
529
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
533 * happened.
534 **************************************************************/
535int rel_accept_lock() {
536
537	struct flock    lock_st;
538	int             rc;
539
540
541	if (!(flags & USE_ACCEPT_LOCK))
542		return 0;
543
544	lock_st.l_type = F_UNLCK;
545	lock_st.l_start = 0;
546	lock_st.l_whence = SEEK_SET;
547	lock_st.l_len = 1;
548
549	errno = 0;
550
551	do {
552		rc = fcntl(accept_fd, F_SETLKW, &lock_st);
553	} while (rc != 0 && errno == EINTR);
554
555	if (rc != 0) {
556		rc = errno;
557		logger(L_ERR, L_FUNC, "could not release accept lock");
558		logger(L_ERR, L_FUNC, "fcntl: %s", strerror(rc));
559		return -1;
560	}
561
562	if (flags & VERBOSE)
563		logger(L_DEBUG, L_FUNC, "released accept lock");
564
565	return 0;
566}
567
568
569
570#endif /* USE_UNIX_IPC */
571