GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / saslauthd / ipc_unix.c
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  **************************************************************/
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  **************************************************************/
216 void 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, &len);
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  **************************************************************/
290 void 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  **************************************************************/
323 void 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  **************************************************************/
462 void 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  **************************************************************/
496 int 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  **************************************************************/
535 int 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 */